Implement WASI C API.
This commit implements an initial WASI C API that can be used to instantiate and configure a WASI instance from C. This also implements a `WasiBuilder` for the C# API enabling .NET hosts to bind to Wasmtime's WASI implementation.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ rusty-tags.*
|
||||
*~
|
||||
\#*\#
|
||||
docs/book
|
||||
.vscode/
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1990,7 +1990,9 @@ dependencies = [
|
||||
name = "wasmtime-c-api"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"wasi-common",
|
||||
"wasmtime",
|
||||
"wasmtime-wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -18,3 +18,5 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { path = "../api" }
|
||||
wasi-common = { path = "../wasi-common" }
|
||||
wasmtime-wasi = { path = "../wasi" }
|
||||
|
||||
70
crates/c-api/include/wasi.h
Normal file
70
crates/c-api/include/wasi.h
Normal file
@@ -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(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(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(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
|
||||
@@ -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)]
|
||||
@@ -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)),
|
||||
});
|
||||
@@ -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::*;
|
||||
|
||||
274
crates/c-api/src/wasi.rs
Normal file
274
crates/c-api/src/wasi.rs
Normal file
@@ -0,0 +1,274 @@
|
||||
//! 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,
|
||||
) {
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = 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())),
|
||||
);
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_inherit_argv(config: *mut wasi_config_t) {
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.inherit_args();
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[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);
|
||||
let mut config = Box::from_raw(config);
|
||||
|
||||
for i in 0..envc as usize {
|
||||
config.builder = config.builder.env(
|
||||
CStr::from_ptr(names[i]).to_bytes(),
|
||||
CStr::from_ptr(values[i]).to_bytes(),
|
||||
);
|
||||
}
|
||||
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_inherit_env(config: *mut wasi_config_t) {
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.inherit_env();
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_set_stdin(
|
||||
config: *mut wasi_config_t,
|
||||
path: *const c_char,
|
||||
) -> bool {
|
||||
let file = match open_file(path) {
|
||||
Some(f) => f,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.stdin(file);
|
||||
std::mem::forget(config);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_inherit_stdin(config: *mut wasi_config_t) {
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.inherit_stdin();
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_set_stdout(
|
||||
config: *mut wasi_config_t,
|
||||
path: *const c_char,
|
||||
) -> bool {
|
||||
let file = match create_file(path) {
|
||||
Some(f) => f,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.stdout(file);
|
||||
std::mem::forget(config);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_inherit_stdout(config: *mut wasi_config_t) {
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.inherit_stdout();
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_set_stderr(
|
||||
config: *mut wasi_config_t,
|
||||
path: *const c_char,
|
||||
) -> bool {
|
||||
let file = match create_file(path) {
|
||||
Some(f) => f,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.stderr(file);
|
||||
std::mem::forget(config);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasi_config_inherit_stderr(config: *mut wasi_config_t) {
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.inherit_stderr();
|
||||
std::mem::forget(config);
|
||||
}
|
||||
|
||||
#[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,
|
||||
};
|
||||
|
||||
let mut config = Box::from_raw(config);
|
||||
config.builder = config.builder.preopened_dir(dir, guest_path);
|
||||
std::mem::forget(config);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasi_instance_t {
|
||||
wasi: Wasi,
|
||||
export_cache: HashMap<String, *mut wasm_extern_t>,
|
||||
}
|
||||
|
||||
impl Drop for wasi_instance_t {
|
||||
fn drop(&mut self) {
|
||||
for v in self.export_cache.values() {
|
||||
drop(unsafe { Box::from_raw(*v) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 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().params() != func_type.params() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
if export.ty().results() != func_type.results() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
*(*instance)
|
||||
.export_cache
|
||||
.entry(name.to_string())
|
||||
.or_insert_with(|| {
|
||||
Box::into_raw(Box::new(wasm_extern_t {
|
||||
which: ExternHost::Func(HostRef::new(export.clone())),
|
||||
}))
|
||||
})
|
||||
}
|
||||
None => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
@@ -142,13 +142,12 @@ namespace Tutorial
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
using (var engine = new Engine())
|
||||
using (var store = engine.CreateStore())
|
||||
using (var module = store.CreateModule("hello.wasm"))
|
||||
using (dynamic instance = module.Instantiate(new Host()))
|
||||
{
|
||||
instance.run();
|
||||
}
|
||||
using var engine = new Engine();
|
||||
using var store = engine.CreateStore();
|
||||
using var module = store.CreateModule("hello.wasm");
|
||||
using dynamic instance = module.Instantiate(new Host());
|
||||
|
||||
instance.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ namespace HelloExample
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
using (var engine = new Engine())
|
||||
using (var store = engine.CreateStore())
|
||||
using (var module = store.CreateModule("global.wasm"))
|
||||
using (dynamic instance = module.Instantiate(new Host()))
|
||||
{
|
||||
instance.run(20);
|
||||
}
|
||||
using var engine = new Engine();
|
||||
using var store = engine.CreateStore();
|
||||
using var module = store.CreateModule("global.wasm");
|
||||
using dynamic instance = module.Instantiate(new Host());
|
||||
|
||||
instance.run(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,12 @@ namespace HelloExample
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
using (var engine = new Engine())
|
||||
using (var store = engine.CreateStore())
|
||||
using (var module = store.CreateModule("hello.wasm"))
|
||||
using (dynamic instance = module.Instantiate(new Host()))
|
||||
{
|
||||
instance.run();
|
||||
}
|
||||
using var engine = new Engine();
|
||||
using var store = engine.CreateStore();
|
||||
using var module = store.CreateModule("hello.wasm");
|
||||
using dynamic instance = module.Instantiate(new Host());
|
||||
|
||||
instance.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@ namespace HelloExample
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
using (var engine = new Engine())
|
||||
using (var store = engine.CreateStore())
|
||||
using (var module = store.CreateModule("memory.wasm"))
|
||||
using (dynamic instance = module.Instantiate(new Host()))
|
||||
{
|
||||
instance.run();
|
||||
}
|
||||
using var engine = new Engine();
|
||||
using var store = engine.CreateStore();
|
||||
using var module = store.CreateModule("memory.wasm");
|
||||
using dynamic instance = module.Instantiate(new Host());
|
||||
|
||||
instance.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,35 +10,37 @@ namespace Wasmtime.Bindings
|
||||
/// <summary>
|
||||
/// Represents an abstract host binding.
|
||||
/// </summary>
|
||||
public abstract class Binding
|
||||
internal abstract class Binding
|
||||
{
|
||||
internal abstract SafeHandle Bind(Store store, IHost host);
|
||||
public abstract SafeHandle Bind(Store store, IHost host);
|
||||
|
||||
internal static void ThrowBindingException(Import import, MemberInfo member, string message)
|
||||
public static WasmtimeException CreateBindingException(Import import, MemberInfo member, string message)
|
||||
{
|
||||
throw new WasmtimeException($"Unable to bind '{member.DeclaringType.Name}.{member.Name}' to WebAssembly import '{import}': {message}.");
|
||||
return new WasmtimeException($"Unable to bind '{member.DeclaringType.Name}.{member.Name}' to WebAssembly import '{import}': {message}.");
|
||||
}
|
||||
|
||||
internal static List<Binding> GetImportBindings(IHost host, Module module)
|
||||
public static List<Binding> GetImportBindings(Module module, Wasi wasi = null, IHost host = null)
|
||||
{
|
||||
if (host is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
if (module is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(module));
|
||||
}
|
||||
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
|
||||
var type = host.GetType();
|
||||
var methods = type.GetMethods(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
|
||||
var fields = type.GetFields(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
|
||||
|
||||
var bindings = new List<Binding>();
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
|
||||
var type = host?.GetType();
|
||||
var methods = type?.GetMethods(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
|
||||
var fields = type?.GetFields(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
|
||||
|
||||
foreach (var import in module.Imports.All)
|
||||
{
|
||||
var wasiBinding = wasi?.Bind(import);
|
||||
if (!(wasiBinding is null))
|
||||
{
|
||||
bindings.Add(wasiBinding);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (import)
|
||||
{
|
||||
case FunctionImport func:
|
||||
@@ -63,7 +65,7 @@ namespace Wasmtime.Bindings
|
||||
|
||||
private static FunctionBinding BindFunction(FunctionImport import, IEnumerable<MethodInfo> methods)
|
||||
{
|
||||
var method = methods.Where(m =>
|
||||
var method = methods?.Where(m =>
|
||||
{
|
||||
var attribute = (ImportAttribute)m.GetCustomAttribute(typeof(ImportAttribute));
|
||||
if (attribute is null)
|
||||
@@ -88,7 +90,7 @@ namespace Wasmtime.Bindings
|
||||
|
||||
private static GlobalBinding BindGlobal(GlobalImport import, IEnumerable<FieldInfo> fields)
|
||||
{
|
||||
var field = fields.Where(f =>
|
||||
var field = fields?.Where(f =>
|
||||
{
|
||||
var attribute = (ImportAttribute)f.GetCustomAttribute(typeof(ImportAttribute));
|
||||
return attribute.Name == import.Name &&
|
||||
@@ -108,7 +110,7 @@ namespace Wasmtime.Bindings
|
||||
|
||||
private static MemoryBinding BindMemory(MemoryImport import, IEnumerable<FieldInfo> fields)
|
||||
{
|
||||
var field = fields.Where(f =>
|
||||
var field = fields?.Where(f =>
|
||||
{
|
||||
var attribute = (ImportAttribute)f.GetCustomAttribute(typeof(ImportAttribute));
|
||||
return attribute.Name == import.Name &&
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Wasmtime.Bindings
|
||||
/// <summary>
|
||||
/// Represents a host function binding.
|
||||
/// </summary>
|
||||
public class FunctionBinding : Binding
|
||||
internal class FunctionBinding : Binding
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new function binding.
|
||||
@@ -46,22 +46,19 @@ namespace Wasmtime.Bindings
|
||||
/// </summary>
|
||||
public MethodInfo Method { get; private set; }
|
||||
|
||||
internal override SafeHandle Bind(Store store, IHost host)
|
||||
public override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (_callback != null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot bind more than once.");
|
||||
}
|
||||
|
||||
_callback = CreateCallback(store, host);
|
||||
|
||||
var parameters = Interop.ToValueTypeVec(Import.Parameters);
|
||||
var results = Interop.ToValueTypeVec(Import.Results);
|
||||
using (var funcType = Interop.wasm_functype_new(ref parameters, ref results))
|
||||
{
|
||||
return Interop.wasm_func_new(store.Handle, funcType, _callback);
|
||||
var callback = CreateCallback(store, host);
|
||||
var func = Interop.wasm_func_new(store.Handle, funcType, callback);
|
||||
// Store the callback with the safe handle to keep the delegate GC reachable
|
||||
func.Callback = callback;
|
||||
return func;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,17 +67,17 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (Method.IsStatic)
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method cannot be static");
|
||||
throw CreateBindingException(Import, Method, "method cannot be static");
|
||||
}
|
||||
|
||||
if (Method.IsGenericMethod)
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method cannot be generic");
|
||||
throw CreateBindingException(Import, Method, "method cannot be generic");
|
||||
}
|
||||
|
||||
if (Method.IsConstructor)
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method cannot be a constructor");
|
||||
throw CreateBindingException(Import, Method, "method cannot be a constructor");
|
||||
}
|
||||
|
||||
ValidateParameters();
|
||||
@@ -93,7 +90,7 @@ namespace Wasmtime.Bindings
|
||||
var parameters = Method.GetParameters();
|
||||
if (parameters.Length != Import.Parameters.Count)
|
||||
{
|
||||
ThrowBindingException(
|
||||
throw CreateBindingException(
|
||||
Import,
|
||||
Method,
|
||||
$"parameter mismatch: import requires {Import.Parameters.Count} but the method has {parameters.Length}");
|
||||
@@ -106,18 +103,18 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (parameter.IsOut)
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be an 'out' parameter");
|
||||
throw CreateBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be an 'out' parameter");
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be a 'ref' parameter");
|
||||
throw CreateBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be a 'ref' parameter");
|
||||
}
|
||||
}
|
||||
|
||||
var expected = Import.Parameters[i];
|
||||
if (!Interop.TryGetValueKind(parameter.ParameterType, out var kind) || !Interop.IsMatchingKind(kind, expected))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"method parameter '{parameter.Name}' is expected to be of type '{Interop.ToString(expected)}'");
|
||||
throw CreateBindingException(Import, Method, $"method parameter '{parameter.Name}' is expected to be of type '{Interop.ToString(expected)}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +126,7 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (Method.ReturnType != typeof(void))
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method must return void");
|
||||
throw CreateBindingException(Import, Method, "method must return void");
|
||||
}
|
||||
}
|
||||
else if (resultsCount == 1)
|
||||
@@ -137,14 +134,14 @@ namespace Wasmtime.Bindings
|
||||
var expected = Import.Results[0];
|
||||
if (!Interop.TryGetValueKind(Method.ReturnType, out var kind) || !Interop.IsMatchingKind(kind, expected))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"return type is expected to be '{Interop.ToString(expected)}'");
|
||||
throw CreateBindingException(Import, Method, $"return type is expected to be '{Interop.ToString(expected)}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsTupleOfSize(Method.ReturnType, resultsCount))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"return type is expected to be a tuple of size {resultsCount}");
|
||||
throw CreateBindingException(Import, Method, $"return type is expected to be a tuple of size {resultsCount}");
|
||||
}
|
||||
|
||||
var typeArguments =
|
||||
@@ -163,7 +160,7 @@ namespace Wasmtime.Bindings
|
||||
var expected = Import.Results[i];
|
||||
if (!Interop.TryGetValueKind(typeArgument, out var kind) || !Interop.IsMatchingKind(kind, expected))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"return tuple item #{i} is expected to be of type '{Interop.ToString(expected)}'");
|
||||
throw CreateBindingException(Import, Method, $"return tuple item #{i} is expected to be of type '{Interop.ToString(expected)}'");
|
||||
}
|
||||
|
||||
++i;
|
||||
@@ -340,7 +337,5 @@ namespace Wasmtime.Bindings
|
||||
throw new NotSupportedException("Unsupported return value type.");
|
||||
}
|
||||
}
|
||||
|
||||
private Interop.WasmFuncCallback _callback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Wasmtime.Bindings
|
||||
/// <summary>
|
||||
/// Represents a host global binding.
|
||||
/// </summary>
|
||||
public class GlobalBinding : Binding
|
||||
internal class GlobalBinding : Binding
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new global binding.
|
||||
@@ -43,12 +43,12 @@ namespace Wasmtime.Bindings
|
||||
/// </summary>
|
||||
public FieldInfo Field { get; private set; }
|
||||
|
||||
internal override SafeHandle Bind(Store store, IHost host)
|
||||
public override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
dynamic global = Field.GetValue(host);
|
||||
if (global.Handle != null)
|
||||
if (!(global.Handle is null))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot bind more than once.");
|
||||
}
|
||||
@@ -74,17 +74,17 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (Field.IsStatic)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field cannot be static");
|
||||
throw CreateBindingException(Import, Field, "field cannot be static");
|
||||
}
|
||||
|
||||
if (!Field.IsInitOnly)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field must be readonly");
|
||||
throw CreateBindingException(Import, Field, "field must be readonly");
|
||||
}
|
||||
|
||||
if (!Field.FieldType.IsGenericType)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field is expected to be of type 'Global<T>'");
|
||||
throw CreateBindingException(Import, Field, "field is expected to be of type 'Global<T>'");
|
||||
}
|
||||
|
||||
var definition = Field.FieldType.GetGenericTypeDefinition();
|
||||
@@ -92,19 +92,19 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (Import.IsMutable)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "the import is mutable (use the 'MutableGlobal' type)");
|
||||
throw CreateBindingException(Import, Field, "the import is mutable (use the 'MutableGlobal' type)");
|
||||
}
|
||||
}
|
||||
else if (definition == typeof(MutableGlobal<>))
|
||||
{
|
||||
if (!Import.IsMutable)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "the import is constant (use the 'Global' type)");
|
||||
throw CreateBindingException(Import, Field, "the import is constant (use the 'Global' type)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field is expected to be of type 'Global<T>' or 'MutableGlobal<T>'");
|
||||
throw CreateBindingException(Import, Field, "field is expected to be of type 'Global<T>' or 'MutableGlobal<T>'");
|
||||
}
|
||||
|
||||
var arg = Field.FieldType.GetGenericArguments()[0];
|
||||
@@ -113,12 +113,12 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (!Interop.IsMatchingKind(kind, Import.Kind))
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"global type argument is expected to be of type '{Interop.ToString(Import.Kind)}'");
|
||||
throw CreateBindingException(Import, Field, $"global type argument is expected to be of type '{Interop.ToString(Import.Kind)}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"'{arg}' is not a valid global type");
|
||||
throw CreateBindingException(Import, Field, $"'{arg}' is not a valid global type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Wasmtime.Bindings
|
||||
/// <summary>
|
||||
/// Represents a host memory binding.
|
||||
/// </summary>
|
||||
public class MemoryBinding : Binding
|
||||
internal class MemoryBinding : Binding
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new memory binding.
|
||||
@@ -43,10 +43,10 @@ namespace Wasmtime.Bindings
|
||||
/// </summary>
|
||||
public FieldInfo Field { get; private set; }
|
||||
|
||||
internal override SafeHandle Bind(Store store, IHost host)
|
||||
public override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
Memory memory = (Memory)Field.GetValue(host);
|
||||
if (memory.Handle != null)
|
||||
if (!(memory.Handle is null))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot bind more than once.");
|
||||
}
|
||||
@@ -56,11 +56,11 @@ namespace Wasmtime.Bindings
|
||||
|
||||
if (min != Import.Minimum)
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"Memory does not have the expected minimum of {Import.Minimum} page(s)");
|
||||
throw CreateBindingException(Import, Field, $"Memory does not have the expected minimum of {Import.Minimum} page(s)");
|
||||
}
|
||||
if (max != Import.Maximum)
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"Memory does not have the expected maximum of {Import.Maximum} page(s)");
|
||||
throw CreateBindingException(Import, Field, $"Memory does not have the expected maximum of {Import.Maximum} page(s)");
|
||||
}
|
||||
|
||||
unsafe
|
||||
@@ -81,17 +81,17 @@ namespace Wasmtime.Bindings
|
||||
{
|
||||
if (Field.IsStatic)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field cannot be static");
|
||||
throw CreateBindingException(Import, Field, "field cannot be static");
|
||||
}
|
||||
|
||||
if (!Field.IsInitOnly)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field must be readonly");
|
||||
throw CreateBindingException(Import, Field, "field must be readonly");
|
||||
}
|
||||
|
||||
if (Field.FieldType != typeof(Memory))
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field is expected to be of type 'Memory'");
|
||||
throw CreateBindingException(Import, Field, "field is expected to be of type 'Memory'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
crates/misc/dotnet/src/Bindings/WasiBinding.cs
Normal file
23
crates/misc/dotnet/src/Bindings/WasiBinding.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Wasmtime.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a binding to a WASI export.
|
||||
/// </summary>
|
||||
internal class WasiBinding : Binding
|
||||
{
|
||||
public WasiBinding(IntPtr handle)
|
||||
{
|
||||
_handle = handle;
|
||||
}
|
||||
|
||||
public override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
return new Interop.WasiExportHandle(_handle);
|
||||
}
|
||||
|
||||
private IntPtr _handle;
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,23 @@ namespace Wasmtime.Externs
|
||||
return Encoding.UTF8.GetString(Span.Slice(address, length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a null-terminated UTF-8 string from memory.
|
||||
/// </summary>
|
||||
/// <param name="address">The zero-based address to read from.</param>
|
||||
/// <returns>Returns the string read from memory.</returns>
|
||||
public string ReadNullTerminatedString(int address)
|
||||
{
|
||||
var slice = Span.Slice(address);
|
||||
var terminator = slice.IndexOf((byte)0);
|
||||
if (terminator == -1)
|
||||
{
|
||||
throw new InvalidOperationException("string is not null terminated");
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(slice.Slice(0, terminator));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a UTF-8 string at the given address.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,12 +14,5 @@ namespace Wasmtime
|
||||
/// </summary>
|
||||
/// <remarks>A host can only bind to one module instance at a time.</remarks>
|
||||
Instance Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the import bindings of the host given a WebAssembly module.
|
||||
/// </summary>
|
||||
/// <param name="module">The WebAssembly module to get the import bindings for.</param>
|
||||
/// <returns>Returns the list of import bindings for the host.</returns>
|
||||
List<Binding> GetImportBindings(Module module) => Binding.GetImportBindings(this, module);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ namespace Wasmtime.Imports
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var moduleName = Interop.wasm_importtype_module(importType);
|
||||
Handle = importType;
|
||||
|
||||
var moduleName = Interop.wasm_importtype_module(Handle);
|
||||
ModuleName = Marshal.PtrToStringUTF8((IntPtr)moduleName->data, (int)moduleName->size);
|
||||
|
||||
var name = Interop.wasm_importtype_name(importType);
|
||||
var name = Interop.wasm_importtype_name(Handle);
|
||||
Name = Marshal.PtrToStringUTF8((IntPtr)name->data, (int)name->size);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +32,8 @@ namespace Wasmtime.Imports
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
internal IntPtr Handle { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -6,69 +6,72 @@ namespace Wasmtime.Imports
|
||||
/// <summary>
|
||||
/// Represents imported functions, globals, tables, and memories to a WebAssembly module.
|
||||
/// </summary>
|
||||
public class Imports
|
||||
public class Imports : IDisposable
|
||||
{
|
||||
internal Imports(Module module)
|
||||
{
|
||||
Interop.wasm_importtype_vec_t imports;
|
||||
Interop.wasm_module_imports(module.Handle, out imports);
|
||||
|
||||
try
|
||||
var all = new List<Import>((int)imports.size);
|
||||
var functions = new List<FunctionImport>();
|
||||
var globals = new List<GlobalImport>();
|
||||
var tables = new List<TableImport>();
|
||||
var memories = new List<MemoryImport>();
|
||||
|
||||
for (int i = 0; i < (int)imports.size; ++i)
|
||||
{
|
||||
var all = new List<Import>((int)imports.size);
|
||||
var functions = new List<FunctionImport>();
|
||||
var globals = new List<GlobalImport>();
|
||||
var tables = new List<TableImport>();
|
||||
var memories = new List<MemoryImport>();
|
||||
|
||||
for (int i = 0; i < (int)imports.size; ++i)
|
||||
unsafe
|
||||
{
|
||||
unsafe
|
||||
var importType = imports.data[i];
|
||||
var externType = Interop.wasm_importtype_type(importType);
|
||||
|
||||
switch (Interop.wasm_externtype_kind(externType))
|
||||
{
|
||||
var importType = imports.data[i];
|
||||
var externType = Interop.wasm_importtype_type(importType);
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_FUNC:
|
||||
var function = new FunctionImport(importType, externType);
|
||||
functions.Add(function);
|
||||
all.Add(function);
|
||||
break;
|
||||
|
||||
switch (Interop.wasm_externtype_kind(externType))
|
||||
{
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_FUNC:
|
||||
var function = new FunctionImport(importType, externType);
|
||||
functions.Add(function);
|
||||
all.Add(function);
|
||||
break;
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL:
|
||||
var global = new GlobalImport(importType, externType);
|
||||
globals.Add(global);
|
||||
all.Add(global);
|
||||
break;
|
||||
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL:
|
||||
var global = new GlobalImport(importType, externType);
|
||||
globals.Add(global);
|
||||
all.Add(global);
|
||||
break;
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_TABLE:
|
||||
var table = new TableImport(importType, externType);
|
||||
tables.Add(table);
|
||||
all.Add(table);
|
||||
break;
|
||||
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_TABLE:
|
||||
var table = new TableImport(importType, externType);
|
||||
tables.Add(table);
|
||||
all.Add(table);
|
||||
break;
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_MEMORY:
|
||||
var memory = new MemoryImport(importType, externType);
|
||||
memories.Add(memory);
|
||||
all.Add(memory);
|
||||
break;
|
||||
|
||||
case Interop.wasm_externkind_t.WASM_EXTERN_MEMORY:
|
||||
var memory = new MemoryImport(importType, externType);
|
||||
memories.Add(memory);
|
||||
all.Add(memory);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported import extern type.");
|
||||
}
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported import extern type.");
|
||||
}
|
||||
}
|
||||
|
||||
Functions = functions;
|
||||
Globals = globals;
|
||||
Tables = tables;
|
||||
Memories = memories;
|
||||
All = all;
|
||||
}
|
||||
finally
|
||||
|
||||
Functions = functions;
|
||||
Globals = globals;
|
||||
Tables = tables;
|
||||
Memories = memories;
|
||||
All = all;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
if (!(_imports.data is null))
|
||||
{
|
||||
Interop.wasm_importtype_vec_delete(ref imports);
|
||||
Interop.wasm_importtype_vec_delete(ref _imports);
|
||||
_imports.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,5 +96,7 @@ namespace Wasmtime.Imports
|
||||
public IReadOnlyList<MemoryImport> Memories { get; private set; }
|
||||
|
||||
internal IReadOnlyList<Import> All { get; private set; }
|
||||
|
||||
private Interop.wasm_importtype_vec_t _imports;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Dynamic;
|
||||
using Wasmtime.Externs;
|
||||
using Wasmtime.Bindings;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
@@ -12,22 +13,23 @@ namespace Wasmtime
|
||||
/// </summary>
|
||||
public class Instance : DynamicObject, IDisposable
|
||||
{
|
||||
internal Instance(Module module, IHost host)
|
||||
internal Instance(Module module, Wasi wasi = null, IHost host = null)
|
||||
{
|
||||
Host = host;
|
||||
Module = module;
|
||||
|
||||
//Save the bindings to root the objects.
|
||||
//Otherwise the GC may collect the delegates from ExternFunction for example.
|
||||
_bindings = host.GetImportBindings(module);
|
||||
var handles = _bindings.Select(b => b.Bind(module.Store, host)).ToList();
|
||||
// Save the bindings to root the objects.
|
||||
// Otherwise the GC may collect the callback delegates from FunctionHandles for example.
|
||||
_bindings = Binding.GetImportBindings(module, wasi, host)
|
||||
.Select(b => b.Bind(module.Store, host))
|
||||
.ToArray();
|
||||
|
||||
unsafe
|
||||
{
|
||||
Handle = Interop.wasm_instance_new(
|
||||
Module.Store.Handle,
|
||||
Module.Handle,
|
||||
handles.Select(h => ToExtern(h)).ToArray(),
|
||||
_bindings.Select(h => ToExtern(h)).ToArray(),
|
||||
out var trap);
|
||||
|
||||
if (trap != IntPtr.Zero)
|
||||
@@ -41,12 +43,6 @@ namespace Wasmtime
|
||||
throw new WasmtimeException($"Failed to instantiate module '{module.Name}'.");
|
||||
}
|
||||
|
||||
// Dispose of all function handles (not needed at runtime)
|
||||
foreach (var h in handles.Where(h => h is Interop.FunctionHandle))
|
||||
{
|
||||
h.Dispose();
|
||||
}
|
||||
|
||||
Interop.wasm_instance_exports(Handle, out _externs);
|
||||
|
||||
Externs = new Wasmtime.Externs.Externs(Module.Exports, _externs);
|
||||
@@ -71,17 +67,27 @@ namespace Wasmtime
|
||||
public Wasmtime.Externs.Externs Externs { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
if (!Handle.IsInvalid)
|
||||
{
|
||||
Handle.Dispose();
|
||||
Handle.SetHandleAsInvalid();
|
||||
}
|
||||
if (_externs.size != UIntPtr.Zero)
|
||||
|
||||
if (!(_bindings is null))
|
||||
{
|
||||
foreach (var binding in _bindings)
|
||||
{
|
||||
binding.Dispose();
|
||||
}
|
||||
_bindings = null;
|
||||
}
|
||||
|
||||
if (!(_externs.data is null))
|
||||
{
|
||||
Interop.wasm_extern_vec_delete(ref _externs);
|
||||
_externs.size = UIntPtr.Zero;
|
||||
_externs.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,15 +140,18 @@ namespace Wasmtime
|
||||
case Interop.MemoryHandle m:
|
||||
return Interop.wasm_memory_as_extern(m);
|
||||
|
||||
case Interop.WasiExportHandle w:
|
||||
return w.DangerousGetHandle();
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unexpected handle type.");
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.InstanceHandle Handle { get; private set; }
|
||||
private SafeHandle[] _bindings;
|
||||
private Interop.wasm_extern_vec_t _externs;
|
||||
private Dictionary<string, ExternFunction> _functions;
|
||||
private Dictionary<string, ExternGlobal> _globals;
|
||||
private List<Bindings.Binding> _bindings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
@@ -10,6 +11,8 @@ namespace Wasmtime
|
||||
/// <remarks>See https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.h for the C API reference.</remarks>
|
||||
internal static class Interop
|
||||
{
|
||||
const string LibraryName = "wasmtime";
|
||||
|
||||
internal class EngineHandle : SafeHandle
|
||||
{
|
||||
public EngineHandle() : base(IntPtr.Zero, true)
|
||||
@@ -61,6 +64,8 @@ namespace Wasmtime
|
||||
{
|
||||
}
|
||||
|
||||
public WasmFuncCallback Callback { get; set; } = null;
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
@@ -175,6 +180,51 @@ namespace Wasmtime
|
||||
}
|
||||
}
|
||||
|
||||
internal class WasiConfigHandle : SafeHandle
|
||||
{
|
||||
public WasiConfigHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasi_config_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class WasiInstanceHandle : SafeHandle
|
||||
{
|
||||
public WasiInstanceHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasi_instance_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class WasiExportHandle : SafeHandle
|
||||
{
|
||||
public WasiExportHandle(IntPtr handle) : base(IntPtr.Zero, false /* not owned */)
|
||||
{
|
||||
SetHandle(handle);
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_byte_vec_t
|
||||
{
|
||||
@@ -440,323 +490,419 @@ namespace Wasmtime
|
||||
return vec;
|
||||
}
|
||||
|
||||
internal static unsafe (byte*[], GCHandle[]) ToUTF8PtrArray(IList<string> strings)
|
||||
{
|
||||
// Unfortunately .NET cannot currently marshal string[] as UTF-8
|
||||
// See: https://github.com/dotnet/runtime/issues/7315
|
||||
// Therefore, we need to marshal the strings manually
|
||||
var handles = new GCHandle[strings.Count];
|
||||
for (int i = 0; i < strings.Count; ++i)
|
||||
{
|
||||
handles[i] = GCHandle.Alloc(
|
||||
Encoding.UTF8.GetBytes(strings[i]),
|
||||
GCHandleType.Pinned
|
||||
);
|
||||
}
|
||||
|
||||
var ptrs = new byte*[strings.Count];
|
||||
for (int i = 0; i < strings.Count; ++i)
|
||||
{
|
||||
ptrs[i] = (byte*)handles[i].AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
return (ptrs, handles);
|
||||
}
|
||||
|
||||
// Engine imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern EngineHandle wasm_engine_new();
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_engine_delete(IntPtr engine);
|
||||
|
||||
// Store imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern StoreHandle wasm_store_new(EngineHandle engine);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_store_delete(IntPtr engine);
|
||||
|
||||
// Byte vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_byte_vec_new_empty(out wasm_byte_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_byte_vec_new_uninitialized(out wasm_byte_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_byte_vec_new(out wasm_byte_vec_t vec, UIntPtr length, byte[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_byte_vec_copy(out wasm_byte_vec_t vec, ref wasm_byte_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_byte_vec_delete(ref wasm_byte_vec_t vec);
|
||||
|
||||
// Value type vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_valtype_vec_new_empty(out wasm_valtype_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_valtype_vec_new_uninitialized(out wasm_valtype_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_valtype_vec_new(out wasm_valtype_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_valtype_vec_copy(out wasm_valtype_vec_t vec, ref wasm_valtype_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_valtype_vec_delete(ref wasm_valtype_vec_t vec);
|
||||
|
||||
// Extern vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_extern_vec_new_empty(out wasm_extern_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_extern_vec_new_uninitialized(out wasm_extern_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_extern_vec_new(out wasm_extern_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_extern_vec_copy(out wasm_extern_vec_t vec, ref wasm_extern_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_extern_vec_delete(ref wasm_extern_vec_t vec);
|
||||
|
||||
// Import type vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_importtype_vec_new_empty(out wasm_importtype_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_importtype_vec_new_uninitialized(out wasm_importtype_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_importtype_vec_new(out wasm_importtype_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_importtype_vec_copy(out wasm_importtype_vec_t vec, ref wasm_importtype_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_importtype_vec_delete(ref wasm_importtype_vec_t vec);
|
||||
|
||||
// Export type vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_exporttype_vec_new_empty(out wasm_exporttype_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_exporttype_vec_new_uninitialized(out wasm_exporttype_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_exporttype_vec_new(out wasm_exporttype_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_exporttype_vec_copy(out wasm_exporttype_vec_t vec, ref wasm_exporttype_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_exporttype_vec_delete(ref wasm_exporttype_vec_t vec);
|
||||
|
||||
// Import type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe wasm_byte_vec_t* wasm_importtype_module(IntPtr importType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe wasm_byte_vec_t* wasm_importtype_name(IntPtr importType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe IntPtr wasm_importtype_type(IntPtr importType);
|
||||
|
||||
// Export type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe wasm_byte_vec_t* wasm_exporttype_name(IntPtr exportType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe IntPtr wasm_exporttype_type(IntPtr exportType);
|
||||
|
||||
// Module imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern ModuleHandle wasm_module_new(StoreHandle store, ref wasm_byte_vec_t bytes);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_module_imports(ModuleHandle module, out wasm_importtype_vec_t imports);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_module_exports(ModuleHandle module, out wasm_exporttype_vec_t exports);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_module_delete(IntPtr module);
|
||||
|
||||
// Value type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern ValueTypeHandle wasm_valtype_new(wasm_valkind_t kind);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_valtype_delete(IntPtr valueType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern ValueKind wasm_valtype_kind(IntPtr valueType);
|
||||
|
||||
// Extern imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern wasm_externkind_t wasm_extern_kind(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_extern_type(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_extern_as_func(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_extern_as_global(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_extern_as_table(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_extern_as_memory(IntPtr ext);
|
||||
|
||||
// Extern type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern wasm_externkind_t wasm_externtype_kind(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_externtype_as_functype_const(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_externtype_as_globaltype_const(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_externtype_as_tabletype_const(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_externtype_as_memorytype_const(IntPtr externType);
|
||||
|
||||
// Function imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern FunctionHandle wasm_func_new(StoreHandle store, FuncTypeHandle type, WasmFuncCallback callback);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_func_delete(IntPtr function);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern IntPtr wasm_func_call(IntPtr function, wasm_val_t* args, wasm_val_t* results);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_func_as_extern(FunctionHandle function);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_global_as_extern(GlobalHandle global);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_memory_as_extern(MemoryHandle memory);
|
||||
|
||||
// Function type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe wasm_valtype_vec_t* wasm_functype_params(IntPtr funcType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe wasm_valtype_vec_t* wasm_functype_results(IntPtr funcType);
|
||||
|
||||
// Instance imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe InstanceHandle wasm_instance_new(StoreHandle store, ModuleHandle module, IntPtr[] imports, out IntPtr trap);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_instance_delete(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_instance_exports(InstanceHandle instance, out wasm_extern_vec_t exports);
|
||||
|
||||
// Function type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern FuncTypeHandle wasm_functype_new(ref wasm_valtype_vec_t parameters, ref wasm_valtype_vec_t results);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_functype_delete(IntPtr functype);
|
||||
|
||||
// Global type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern GlobalTypeHandle wasm_globaltype_new(IntPtr valueType, wasm_mutability_t mutability);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_globaltype_delete(IntPtr globalType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_globaltype_content(IntPtr globalType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern wasm_mutability_t wasm_globaltype_mutability(IntPtr globalType);
|
||||
|
||||
// Memory type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe MemoryTypeHandle wasm_memorytype_new(wasm_limits_t* limits);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_memorytype_delete(IntPtr memoryType);
|
||||
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe wasm_limits_t* wasm_memorytype_limits(MemoryTypeHandle memoryType);
|
||||
|
||||
// Trap imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_trap_new(StoreHandle store, ref wasm_byte_vec_t message);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_trap_delete(IntPtr trap);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_trap_message(IntPtr trap, out wasm_byte_vec_t message);
|
||||
|
||||
// Table type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_tabletype_element(IntPtr tableType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern wasm_limits_t* wasm_tabletype_limits(IntPtr tableType);
|
||||
|
||||
// Memory type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern wasm_limits_t* wasm_memorytype_limits(IntPtr memoryType);
|
||||
|
||||
// Global imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern GlobalHandle wasm_global_new(StoreHandle handle, GlobalTypeHandle globalType, wasm_val_t* initialValue);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_global_delete(IntPtr global);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_global_type(IntPtr global);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern void wasm_global_get(IntPtr global, wasm_val_t* value);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern void wasm_global_set(IntPtr global, wasm_val_t* value);
|
||||
|
||||
// Memory imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern MemoryHandle wasm_memory_new(StoreHandle handle, MemoryTypeHandle memoryType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasm_memory_delete(IntPtr memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasm_memory_type(MemoryHandle memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static unsafe extern byte* wasm_memory_data(IntPtr memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern UIntPtr wasm_memory_data_size(IntPtr memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern uint wasm_memory_size(MemoryHandle memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
[DllImport(LibraryName)]
|
||||
public static extern bool wasm_memory_grow(MemoryHandle memory, uint delta);
|
||||
|
||||
// WASI config
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern WasiConfigHandle wasi_config_new();
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_config_delete(IntPtr config);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public unsafe static extern void wasi_config_set_argv(WasiConfigHandle config, int argc, byte*[] argv);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_config_inherit_argv(WasiConfigHandle config);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern unsafe void wasi_config_set_env(
|
||||
WasiConfigHandle config,
|
||||
int envc,
|
||||
byte*[] names,
|
||||
byte*[] values
|
||||
);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_config_inherit_env(WasiConfigHandle config);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern bool wasi_config_set_stdin(
|
||||
WasiConfigHandle config,
|
||||
[MarshalAs(UnmanagedType.LPUTF8Str)] string path
|
||||
);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_config_inherit_stdin(WasiConfigHandle config);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern bool wasi_config_set_stdout(
|
||||
WasiConfigHandle config,
|
||||
[MarshalAs(UnmanagedType.LPUTF8Str)] string path
|
||||
);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_config_inherit_stdout(WasiConfigHandle config);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern bool wasi_config_set_stderr(
|
||||
WasiConfigHandle config,
|
||||
[MarshalAs(UnmanagedType.LPUTF8Str)] string path
|
||||
);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_config_inherit_stderr(WasiConfigHandle config);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern bool wasi_config_preopen_dir(
|
||||
WasiConfigHandle config,
|
||||
[MarshalAs(UnmanagedType.LPUTF8Str)] string path,
|
||||
[MarshalAs(UnmanagedType.LPUTF8Str)] string guestPath
|
||||
);
|
||||
|
||||
// WASI instance
|
||||
[DllImport(LibraryName)]
|
||||
public static extern WasiInstanceHandle wasi_instance_new(
|
||||
StoreHandle store,
|
||||
WasiConfigHandle config,
|
||||
out IntPtr trap
|
||||
);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern void wasi_instance_delete(IntPtr instance);
|
||||
|
||||
[DllImport(LibraryName)]
|
||||
public static extern IntPtr wasi_instance_bind_import(WasiInstanceHandle instance, IntPtr importType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,20 +49,33 @@ namespace Wasmtime
|
||||
/// </summary>
|
||||
/// <param name="host">The host to use for the WebAssembly module's instance.</param>
|
||||
/// <returns>Returns a new <see cref="Instance" />.</returns>
|
||||
public Instance Instantiate(IHost host)
|
||||
public Instance Instantiate(IHost host = null)
|
||||
{
|
||||
if (host is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
return Instantiate(null, host);
|
||||
}
|
||||
|
||||
if (host.Instance != null)
|
||||
/// <summary>
|
||||
/// Instantiates a WebAssembly module for the given host.
|
||||
/// </summary>
|
||||
/// <param name="wasi">The WASI instance to use for WASI imports.</param>
|
||||
/// <param name="host">The host to use for the WebAssembly module's instance.</param>
|
||||
/// <returns>Returns a new <see cref="Instance" />.</returns>
|
||||
public Instance Instantiate(Wasi wasi, IHost host = null)
|
||||
{
|
||||
if (!(host?.Instance is null))
|
||||
{
|
||||
throw new InvalidOperationException("The host has already been associated with an instantiated module.");
|
||||
}
|
||||
|
||||
host.Instance = new Instance(this, host);
|
||||
return host.Instance;
|
||||
var instance = new Instance(this, wasi, host);
|
||||
|
||||
if (!(host is null))
|
||||
{
|
||||
host.Instance = instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +107,11 @@ namespace Wasmtime
|
||||
Handle.Dispose();
|
||||
Handle.SetHandleAsInvalid();
|
||||
}
|
||||
if (!(Imports is null))
|
||||
{
|
||||
Imports.Dispose();
|
||||
Imports = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.ModuleHandle Handle { get; private set; }
|
||||
|
||||
@@ -29,9 +29,11 @@ namespace Wasmtime
|
||||
Interop.wasm_trap_message(trap, out var bytes);
|
||||
var byteSpan = new ReadOnlySpan<byte>(bytes.data, checked((int)bytes.size));
|
||||
|
||||
int indexOfNull = byteSpan.IndexOf((byte)0);
|
||||
int indexOfNull = byteSpan.LastIndexOf((byte)0);
|
||||
if (indexOfNull != -1)
|
||||
{
|
||||
byteSpan = byteSpan.Slice(0, indexOfNull);
|
||||
}
|
||||
|
||||
var message = Encoding.UTF8.GetString(byteSpan);
|
||||
Interop.wasm_byte_vec_delete(ref bytes);
|
||||
|
||||
44
crates/misc/dotnet/src/Wasi.cs
Normal file
44
crates/misc/dotnet/src/Wasi.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Wasmtime.Bindings;
|
||||
using Wasmtime.Imports;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
public class Wasi
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a default <see cref="Wasi"/> instance.
|
||||
/// </summary>
|
||||
public Wasi(Store store) :
|
||||
this(
|
||||
(store ?? throw new ArgumentNullException(nameof(store))).Handle,
|
||||
Interop.wasi_config_new()
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
internal Wasi(Interop.StoreHandle store, Interop.WasiConfigHandle config)
|
||||
{
|
||||
IntPtr trap;
|
||||
Handle = Interop.wasi_instance_new(store, config, out trap);
|
||||
config.SetHandleAsInvalid();
|
||||
|
||||
if (trap != IntPtr.Zero)
|
||||
{
|
||||
throw TrapException.FromOwnedTrap(trap);
|
||||
}
|
||||
}
|
||||
|
||||
internal WasiBinding Bind(Import import)
|
||||
{
|
||||
var export = Interop.wasi_instance_bind_import(Handle, import.Handle);
|
||||
if (export == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new WasiBinding(export);
|
||||
}
|
||||
|
||||
internal Interop.WasiInstanceHandle Handle { get; private set; }
|
||||
}
|
||||
}
|
||||
409
crates/misc/dotnet/src/WasiBuilder.cs
Normal file
409
crates/misc/dotnet/src/WasiBuilder.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a build of WASI instances.
|
||||
/// </summary>
|
||||
public class WasiBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="WasiBuilder" />.
|
||||
/// </summary>
|
||||
public WasiBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command line argument to the builder.
|
||||
/// </summary>
|
||||
/// <param name="arg">The command line argument to add.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithArg(string arg)
|
||||
{
|
||||
if (arg is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(arg));
|
||||
}
|
||||
|
||||
if (_inheritArgs)
|
||||
{
|
||||
_args.Clear();
|
||||
_inheritArgs = false;
|
||||
}
|
||||
|
||||
_args.Add(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple command line arguments to the builder.
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments to add.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithArgs(IEnumerable<string> args)
|
||||
{
|
||||
if (args is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
if (_inheritArgs)
|
||||
{
|
||||
_args.Clear();
|
||||
_inheritArgs = false;
|
||||
}
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
_args.Add(arg);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple command line arguments to the builder.
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments to add.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithArgs(params string[] args)
|
||||
{
|
||||
return WithArgs((IEnumerable<string>)args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to inherit command line arguments.
|
||||
/// </summary>
|
||||
/// <remarks>Any explicitly specified command line arguments will be removed.</remarks>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithInheritedArgs()
|
||||
{
|
||||
_inheritArgs = true;
|
||||
_args.Clear();
|
||||
_args.AddRange(Environment.GetCommandLineArgs());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an environment variable to the builder.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the environment variable.</param>
|
||||
/// <param name="value">The value of the environment variable.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithEnvironmentVariable(string name, string value)
|
||||
{
|
||||
if (name is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentException("Environment variable name cannot be empty.", nameof(name));
|
||||
}
|
||||
|
||||
_inheritEnv = false;
|
||||
_vars.Add((name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple environment variables to the builder.
|
||||
/// </summary>
|
||||
/// <param name="vars">The name-value tuples of the environment variables to add.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithEnvironmentVariables(IEnumerable<(string,string)> vars)
|
||||
{
|
||||
if (vars is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(vars));
|
||||
}
|
||||
|
||||
_inheritEnv = false;
|
||||
|
||||
foreach (var v in vars)
|
||||
{
|
||||
_vars.Add(v);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to inherit environment variables.
|
||||
/// </summary>
|
||||
/// <remarks>Any explicitly specified environment variables will be removed.</remarks>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithInheritedEnvironment()
|
||||
{
|
||||
_inheritEnv = true;
|
||||
_vars.Clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to use the given file path as stdin.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to use as stdin.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithStandardInput(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
|
||||
}
|
||||
|
||||
_inheritStandardInput = false;
|
||||
_standardInputPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to inherit stdin.
|
||||
/// </summary>
|
||||
/// <remarks>Any explicitly specified stdin file will be removed.</remarks>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithInheritedStandardInput()
|
||||
{
|
||||
_inheritStandardInput = true;
|
||||
_standardInputPath = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to use the given file path as stdout.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to use as stdout.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithStandardOutput(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
|
||||
}
|
||||
|
||||
_inheritStandardOutput = false;
|
||||
_standardOutputPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to inherit stdout.
|
||||
/// </summary>
|
||||
/// <remarks>Any explicitly specified stdout file will be removed.</remarks>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithInheritedStandardOutput()
|
||||
{
|
||||
_inheritStandardOutput = true;
|
||||
_standardOutputPath = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to use the given file path as stderr.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to use as stderr.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithStandardError(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
|
||||
}
|
||||
|
||||
_inheritStandardError = false;
|
||||
_standardErrorPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the builder to inherit stderr.
|
||||
/// </summary>
|
||||
/// <remarks>Any explicitly specified stderr file will be removed.</remarks>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithInheritedStandardError()
|
||||
{
|
||||
_inheritStandardError = true;
|
||||
_standardErrorPath = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a preopen directory to the builder.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the directory to add.</param>
|
||||
/// <param name="guestPath">The path the guest will use to open the directory.</param>
|
||||
/// <returns>Returns the current builder.</returns>
|
||||
public WasiBuilder WithPreopenedDirectory(string path, string guestPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
|
||||
}
|
||||
if (string.IsNullOrEmpty(guestPath))
|
||||
{
|
||||
throw new ArgumentException("The guest path cannot be null or empty.", nameof(guestPath));
|
||||
}
|
||||
|
||||
_preopenDirs.Add((path, guestPath));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the <see cref="Wasi" /> instance.
|
||||
/// </summary>
|
||||
/// <param name="store">The <see cref="Store" /> to use.</param>
|
||||
/// <returns>Returns the new <see cref="Wasi" /> instance.</returns>
|
||||
public Wasi Build(Store store)
|
||||
{
|
||||
var config = Interop.wasi_config_new();
|
||||
|
||||
SetConfigArgs(config);
|
||||
SetEnvironmentVariables(config);
|
||||
SetStandardIn(config);
|
||||
SetStandardOut(config);
|
||||
SetStandardError(config);
|
||||
SetPreopenDirectories(config);
|
||||
|
||||
return new Wasi(store.Handle, config);
|
||||
}
|
||||
|
||||
private unsafe void SetConfigArgs(Interop.WasiConfigHandle config)
|
||||
{
|
||||
// Don't call wasi_config_inherit_argv as the command line to the .NET program may not be
|
||||
// the same as the process' command line (e.g. `dotnet foo.dll foo bar baz` => "foo.dll foo bar baz").
|
||||
if (_args.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var (args, handles) = Interop.ToUTF8PtrArray(_args);
|
||||
|
||||
try
|
||||
{
|
||||
Interop.wasi_config_set_argv(config, _args.Count, args);
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var handle in handles)
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void SetEnvironmentVariables(Interop.WasiConfigHandle config)
|
||||
{
|
||||
if (_inheritEnv)
|
||||
{
|
||||
Interop.wasi_config_inherit_env(config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_vars.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var (names, nameHandles) = Interop.ToUTF8PtrArray(_vars.Select(var => var.Name).ToArray());
|
||||
var (values, valueHandles) = Interop.ToUTF8PtrArray(_vars.Select(var => var.Value).ToArray());
|
||||
|
||||
try
|
||||
{
|
||||
Interop.wasi_config_set_env(config, _vars.Count, names, values);
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var handle in nameHandles)
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
foreach (var handle in valueHandles)
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetStandardIn(Interop.WasiConfigHandle config)
|
||||
{
|
||||
if (_inheritStandardInput)
|
||||
{
|
||||
Interop.wasi_config_inherit_stdin(config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_standardInputPath))
|
||||
{
|
||||
if (!Interop.wasi_config_set_stdin(config, _standardInputPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to set stdin to file '{_standardInputPath}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetStandardOut(Interop.WasiConfigHandle config)
|
||||
{
|
||||
if (_inheritStandardOutput)
|
||||
{
|
||||
Interop.wasi_config_inherit_stdout(config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_standardOutputPath))
|
||||
{
|
||||
if (!Interop.wasi_config_set_stdout(config, _standardOutputPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to set stdout to file '{_standardOutputPath}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetStandardError(Interop.WasiConfigHandle config)
|
||||
{
|
||||
if (_inheritStandardError)
|
||||
{
|
||||
Interop.wasi_config_inherit_stderr(config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_standardErrorPath))
|
||||
{
|
||||
if (!Interop.wasi_config_set_stderr(config, _standardErrorPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to set stderr to file '{_standardErrorPath}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPreopenDirectories(Interop.WasiConfigHandle config)
|
||||
{
|
||||
foreach (var dir in _preopenDirs)
|
||||
{
|
||||
if (!Interop.wasi_config_preopen_dir(config, dir.Path, dir.GuestPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to preopen directory '{dir.Path}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<string> _args = new List<string>();
|
||||
private readonly List<(string Name, string Value)> _vars = new List<(string, string)>();
|
||||
private string _standardInputPath;
|
||||
private string _standardOutputPath;
|
||||
private string _standardErrorPath;
|
||||
private readonly List<(string Path, string GuestPath)> _preopenDirs = new List<(string, string)>();
|
||||
private bool _inheritArgs = false;
|
||||
private bool _inheritEnv = false;
|
||||
private bool _inheritStandardInput = false;
|
||||
private bool _inheritStandardOutput = false;
|
||||
private bool _inheritStandardError = false;
|
||||
}
|
||||
}
|
||||
@@ -15,19 +15,19 @@ namespace Wasmtime.Tests
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Module != null)
|
||||
if (!(Module is null))
|
||||
{
|
||||
Module.Dispose();
|
||||
Module = null;
|
||||
}
|
||||
|
||||
if (Store != null)
|
||||
if (!(Store is null))
|
||||
{
|
||||
Store.Dispose();
|
||||
Store = null;
|
||||
}
|
||||
|
||||
if (Engine != null)
|
||||
if (!(Engine is null))
|
||||
{
|
||||
Engine.Dispose();
|
||||
Engine = null;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Wasmtime.Tests
|
||||
|
||||
public class FunctionThunkingTests : IClassFixture<FunctionThunkingFixture>
|
||||
{
|
||||
const string THROW_MESSAGE = "Test error messages for wasmtime dotnet bidnings unit tests.";
|
||||
const string THROW_MESSAGE = "Test error message for wasmtime dotnet unit tests.";
|
||||
|
||||
class MyHost : IHost
|
||||
{
|
||||
@@ -53,7 +53,7 @@ namespace Wasmtime.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItPropegatesExceptionsToCallersViaTraps()
|
||||
public void ItPropagatesExceptionsToCallersViaTraps()
|
||||
{
|
||||
var host = new MyHost();
|
||||
using (var instance = Fixture.Module.Instantiate(host))
|
||||
|
||||
@@ -113,6 +113,27 @@ impl WasiCtxBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdin from the host process.
|
||||
pub fn inherit_stdin(mut self) -> Self {
|
||||
self.fds
|
||||
.insert(0, PendingFdEntry::Thunk(FdEntry::duplicate_stdin));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdout from the host process.
|
||||
pub fn inherit_stdout(mut self) -> Self {
|
||||
self.fds
|
||||
.insert(1, PendingFdEntry::Thunk(FdEntry::duplicate_stdout));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdout from the host process.
|
||||
pub fn inherit_stderr(mut self) -> Self {
|
||||
self.fds
|
||||
.insert(2, PendingFdEntry::Thunk(FdEntry::duplicate_stderr));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit the stdin, stdout, and stderr streams from the host process.
|
||||
pub fn inherit_stdio(mut self) -> Self {
|
||||
self.fds
|
||||
|
||||
Reference in New Issue
Block a user