Merge pull request #977 from peterhuene/wasi-c-api
Implement a WASI instantiation C API.
This commit is contained in:
@@ -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]>,
|
||||
|
||||
@@ -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_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
|
||||
@@ -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::*;
|
||||
|
||||
239
crates/c-api/src/wasi.rs
Normal file
239
crates/c-api/src/wasi.rs
Normal file
@@ -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(),
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,10 +165,8 @@ Alternatively, the `run` function could be invoked without using the runtime bin
|
||||
|
||||
```c#
|
||||
...
|
||||
using (var instance = module.Instantiate(new Host()))
|
||||
{
|
||||
instance.Externs.Functions[0].Invoke();
|
||||
}
|
||||
using var instance = module.Instantiate(new Host());
|
||||
instance.Externs.Functions[0].Invoke();
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
@@ -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,23 +46,18 @@ 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);
|
||||
}
|
||||
using var funcType = Interop.wasm_functype_new(ref parameters, ref results);
|
||||
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 +65,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 +88,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 +101,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 +124,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 +132,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 +158,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 +335,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.");
|
||||
}
|
||||
@@ -59,14 +59,14 @@ namespace Wasmtime.Bindings
|
||||
var valueTypeHandle = valueType.DangerousGetHandle();
|
||||
valueType.SetHandleAsInvalid();
|
||||
|
||||
using (var globalType = Interop.wasm_globaltype_new(
|
||||
using var globalType = Interop.wasm_globaltype_new(
|
||||
valueTypeHandle,
|
||||
Import.IsMutable ? Interop.wasm_mutability_t.WASM_VAR : Interop.wasm_mutability_t.WASM_CONST))
|
||||
{
|
||||
var handle = Interop.wasm_global_new(store.Handle, globalType, &v);
|
||||
global.Handle = handle;
|
||||
return handle;
|
||||
}
|
||||
Import.IsMutable ? Interop.wasm_mutability_t.WASM_VAR : Interop.wasm_mutability_t.WASM_CONST
|
||||
);
|
||||
|
||||
var handle = Interop.wasm_global_new(store.Handle, globalType, &v);
|
||||
global.Handle = handle;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -68,12 +68,11 @@ namespace Wasmtime.Bindings
|
||||
Interop.wasm_limits_t limits = new Interop.wasm_limits_t();
|
||||
limits.min = min;
|
||||
limits.max = max;
|
||||
using (var memoryType = Interop.wasm_memorytype_new(&limits))
|
||||
{
|
||||
var handle = Interop.wasm_memory_new(store.Handle, memoryType);
|
||||
memory.Handle = handle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
using var memoryType = Interop.wasm_memorytype_new(&limits);
|
||||
var handle = Interop.wasm_memory_new(store.Handle, memoryType);
|
||||
memory.Handle = handle;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,17 +80,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,415 @@ 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];
|
||||
var ptrs = new byte*[strings.Count];
|
||||
for (int i = 0; i < strings.Count; ++i)
|
||||
{
|
||||
handles[i] = GCHandle.Alloc(
|
||||
Encoding.UTF8.GetBytes(strings[i] + '\0'),
|
||||
GCHandleType.Pinned
|
||||
);
|
||||
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_file(
|
||||
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_file(
|
||||
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_file(
|
||||
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_file(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_file(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_file(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
|
||||
{
|
||||
@@ -36,36 +36,34 @@ namespace Wasmtime.Tests
|
||||
public void ItBindsImportMethodsAndCallsThemCorrectly()
|
||||
{
|
||||
var host = new MyHost();
|
||||
using (var instance = Fixture.Module.Instantiate(host))
|
||||
{
|
||||
var add_func = instance.Externs.Functions.Where(f => f.Name == "add_wrapper").Single();
|
||||
int invoke_add(int x, int y) => (int)add_func.Invoke(new object[] { x, y });
|
||||
using var instance = Fixture.Module.Instantiate(host);
|
||||
|
||||
invoke_add(40, 2).Should().Be(42);
|
||||
invoke_add(22, 5).Should().Be(27);
|
||||
var add_func = instance.Externs.Functions.Where(f => f.Name == "add_wrapper").Single();
|
||||
int invoke_add(int x, int y) => (int)add_func.Invoke(new object[] { x, y });
|
||||
|
||||
//Collect garbage to make sure delegate function pointers pasted to wasmtime are rooted.
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
invoke_add(40, 2).Should().Be(42);
|
||||
invoke_add(22, 5).Should().Be(27);
|
||||
|
||||
invoke_add(1970, 50).Should().Be(2020);
|
||||
}
|
||||
//Collect garbage to make sure delegate function pointers pasted to wasmtime are rooted.
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
invoke_add(1970, 50).Should().Be(2020);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItPropegatesExceptionsToCallersViaTraps()
|
||||
public void ItPropagatesExceptionsToCallersViaTraps()
|
||||
{
|
||||
var host = new MyHost();
|
||||
using (var instance = Fixture.Module.Instantiate(host))
|
||||
{
|
||||
var throw_func = instance.Externs.Functions.Where(f => f.Name == "do_throw_wrapper").Single();
|
||||
Action action = () => throw_func.Invoke();
|
||||
using var instance = Fixture.Module.Instantiate(host);
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<TrapException>()
|
||||
.WithMessage(THROW_MESSAGE);
|
||||
}
|
||||
var throw_func = instance.Externs.Functions.Where(f => f.Name == "do_throw_wrapper").Single();
|
||||
Action action = () => throw_func.Invoke();
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<TrapException>()
|
||||
.WithMessage(THROW_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,124 +46,123 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItCreatesExternsForTheGlobals()
|
||||
{
|
||||
using (var instance = Fixture.Module.Instantiate(new Host()))
|
||||
{
|
||||
dynamic dyn = instance;
|
||||
var globals = instance.Externs.Globals;
|
||||
globals.Count.Should().Be(8);
|
||||
using var instance = Fixture.Module.Instantiate(new Host());
|
||||
|
||||
var i32 = globals[0];
|
||||
i32.Name.Should().Be("global_i32");
|
||||
i32.Kind.Should().Be(ValueKind.Int32);
|
||||
i32.IsMutable.Should().Be(false);
|
||||
i32.Value.Should().Be(0);
|
||||
dynamic dyn = instance;
|
||||
var globals = instance.Externs.Globals;
|
||||
globals.Count.Should().Be(8);
|
||||
|
||||
var i32Mut = globals[1];
|
||||
i32Mut.Name.Should().Be("global_i32_mut");
|
||||
i32Mut.Kind.Should().Be(ValueKind.Int32);
|
||||
i32Mut.IsMutable.Should().Be(true);
|
||||
i32Mut.Value.Should().Be(1);
|
||||
i32Mut.Value = 11;
|
||||
i32Mut.Value.Should().Be(11);
|
||||
dyn.global_i32_mut = 12;
|
||||
((int)dyn.global_i32_mut).Should().Be(12);
|
||||
i32Mut.Value.Should().Be(12);
|
||||
var i32 = globals[0];
|
||||
i32.Name.Should().Be("global_i32");
|
||||
i32.Kind.Should().Be(ValueKind.Int32);
|
||||
i32.IsMutable.Should().Be(false);
|
||||
i32.Value.Should().Be(0);
|
||||
|
||||
var i64 = globals[2];
|
||||
i64.Name.Should().Be("global_i64");
|
||||
i64.Kind.Should().Be(ValueKind.Int64);
|
||||
i64.IsMutable.Should().Be(false);
|
||||
i64.Value.Should().Be(2);
|
||||
var i32Mut = globals[1];
|
||||
i32Mut.Name.Should().Be("global_i32_mut");
|
||||
i32Mut.Kind.Should().Be(ValueKind.Int32);
|
||||
i32Mut.IsMutable.Should().Be(true);
|
||||
i32Mut.Value.Should().Be(1);
|
||||
i32Mut.Value = 11;
|
||||
i32Mut.Value.Should().Be(11);
|
||||
dyn.global_i32_mut = 12;
|
||||
((int)dyn.global_i32_mut).Should().Be(12);
|
||||
i32Mut.Value.Should().Be(12);
|
||||
|
||||
var i64Mut = globals[3];
|
||||
i64Mut.Name.Should().Be("global_i64_mut");
|
||||
i64Mut.Kind.Should().Be(ValueKind.Int64);
|
||||
i64Mut.IsMutable.Should().Be(true);
|
||||
i64Mut.Value.Should().Be(3);
|
||||
i64Mut.Value = 13;
|
||||
i64Mut.Value.Should().Be(13);
|
||||
dyn.global_i64_mut = 14;
|
||||
((long)dyn.global_i64_mut).Should().Be(14);
|
||||
i64Mut.Value.Should().Be(14);
|
||||
var i64 = globals[2];
|
||||
i64.Name.Should().Be("global_i64");
|
||||
i64.Kind.Should().Be(ValueKind.Int64);
|
||||
i64.IsMutable.Should().Be(false);
|
||||
i64.Value.Should().Be(2);
|
||||
|
||||
var f32 = globals[4];
|
||||
f32.Name.Should().Be("global_f32");
|
||||
f32.Kind.Should().Be(ValueKind.Float32);
|
||||
f32.IsMutable.Should().Be(false);
|
||||
f32.Value.Should().Be(4);
|
||||
var i64Mut = globals[3];
|
||||
i64Mut.Name.Should().Be("global_i64_mut");
|
||||
i64Mut.Kind.Should().Be(ValueKind.Int64);
|
||||
i64Mut.IsMutable.Should().Be(true);
|
||||
i64Mut.Value.Should().Be(3);
|
||||
i64Mut.Value = 13;
|
||||
i64Mut.Value.Should().Be(13);
|
||||
dyn.global_i64_mut = 14;
|
||||
((long)dyn.global_i64_mut).Should().Be(14);
|
||||
i64Mut.Value.Should().Be(14);
|
||||
|
||||
var f32Mut = globals[5];
|
||||
f32Mut.Name.Should().Be("global_f32_mut");
|
||||
f32Mut.Kind.Should().Be(ValueKind.Float32);
|
||||
f32Mut.IsMutable.Should().Be(true);
|
||||
f32Mut.Value.Should().Be(5);
|
||||
f32Mut.Value = 15;
|
||||
f32Mut.Value.Should().Be(15);
|
||||
dyn.global_f32_mut = 16;
|
||||
((float)dyn.global_f32_mut).Should().Be(16);
|
||||
f32Mut.Value.Should().Be(16);
|
||||
var f32 = globals[4];
|
||||
f32.Name.Should().Be("global_f32");
|
||||
f32.Kind.Should().Be(ValueKind.Float32);
|
||||
f32.IsMutable.Should().Be(false);
|
||||
f32.Value.Should().Be(4);
|
||||
|
||||
var f64 = globals[6];
|
||||
f64.Name.Should().Be("global_f64");
|
||||
f64.Kind.Should().Be(ValueKind.Float64);
|
||||
f64.IsMutable.Should().Be(false);
|
||||
f64.Value.Should().Be(6);
|
||||
var f32Mut = globals[5];
|
||||
f32Mut.Name.Should().Be("global_f32_mut");
|
||||
f32Mut.Kind.Should().Be(ValueKind.Float32);
|
||||
f32Mut.IsMutable.Should().Be(true);
|
||||
f32Mut.Value.Should().Be(5);
|
||||
f32Mut.Value = 15;
|
||||
f32Mut.Value.Should().Be(15);
|
||||
dyn.global_f32_mut = 16;
|
||||
((float)dyn.global_f32_mut).Should().Be(16);
|
||||
f32Mut.Value.Should().Be(16);
|
||||
|
||||
var f64Mut = globals[7];
|
||||
f64Mut.Name.Should().Be("global_f64_mut");
|
||||
f64Mut.Kind.Should().Be(ValueKind.Float64);
|
||||
f64Mut.IsMutable.Should().Be(true);
|
||||
f64Mut.Value.Should().Be(7);
|
||||
f64Mut.Value = 17;
|
||||
f64Mut.Value.Should().Be(17);
|
||||
dyn.global_f64_mut = 17;
|
||||
((double)dyn.global_f64_mut).Should().Be(17);
|
||||
f64Mut.Value.Should().Be(17);
|
||||
var f64 = globals[6];
|
||||
f64.Name.Should().Be("global_f64");
|
||||
f64.Kind.Should().Be(ValueKind.Float64);
|
||||
f64.IsMutable.Should().Be(false);
|
||||
f64.Value.Should().Be(6);
|
||||
|
||||
Action action = () => i32.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i32' cannot be modified.");
|
||||
action = () => dyn.global_i32 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i32' cannot be modified.");
|
||||
var f64Mut = globals[7];
|
||||
f64Mut.Name.Should().Be("global_f64_mut");
|
||||
f64Mut.Kind.Should().Be(ValueKind.Float64);
|
||||
f64Mut.IsMutable.Should().Be(true);
|
||||
f64Mut.Value.Should().Be(7);
|
||||
f64Mut.Value = 17;
|
||||
f64Mut.Value.Should().Be(17);
|
||||
dyn.global_f64_mut = 17;
|
||||
((double)dyn.global_f64_mut).Should().Be(17);
|
||||
f64Mut.Value.Should().Be(17);
|
||||
|
||||
action = () => i64.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i64' cannot be modified.");
|
||||
action = () => dyn.global_i64 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i64' cannot be modified.");
|
||||
Action action = () => i32.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i32' cannot be modified.");
|
||||
action = () => dyn.global_i32 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i32' cannot be modified.");
|
||||
|
||||
action = () => f32.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f32' cannot be modified.");
|
||||
action = () => dyn.global_f32 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f32' cannot be modified.");
|
||||
action = () => i64.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i64' cannot be modified.");
|
||||
action = () => dyn.global_i64 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_i64' cannot be modified.");
|
||||
|
||||
action = () => f64.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f64' cannot be modified.");
|
||||
action = () => dyn.global_f64 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f64' cannot be modified.");
|
||||
}
|
||||
action = () => f32.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f32' cannot be modified.");
|
||||
action = () => dyn.global_f32 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f32' cannot be modified.");
|
||||
|
||||
action = () => f64.Value = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f64' cannot be modified.");
|
||||
action = () => dyn.global_f64 = 0;
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>()
|
||||
.WithMessage("The value of global 'global_f64' cannot be modified.");
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetGlobalExports()
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithMissingImport()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NoImportsHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NoImportsHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -129,7 +129,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithStaticField()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new GlobalIsStaticHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new GlobalIsStaticHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -140,7 +140,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithNonReadOnlyField()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new GlobalIsNotReadOnlyHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new GlobalIsNotReadOnlyHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -151,7 +151,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithInvalidType()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NotAGlobalHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotAGlobalHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -162,7 +162,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithInvalidGlobalType()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NotAValidGlobalTypeHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotAValidGlobalTypeHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -173,7 +173,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithGlobalTypeMismatch()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new TypeMismatchHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new TypeMismatchHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -184,7 +184,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenGlobalIsNotMut()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NotMutHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotMutHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -195,7 +195,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenGlobalIsMut()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new MutHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MutHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -207,53 +207,52 @@ namespace Wasmtime.Tests
|
||||
public void ItBindsTheGlobalsCorrectly()
|
||||
{
|
||||
var host = new ValidHost();
|
||||
using (dynamic instance = Fixture.Module.Instantiate(host))
|
||||
{
|
||||
host.Int32Mut.Value.Should().Be(0);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(0);
|
||||
host.Int32.Value.Should().Be(1);
|
||||
((int)instance.get_global_i32()).Should().Be(1);
|
||||
host.Int64Mut.Value.Should().Be(2);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(2);
|
||||
host.Int64.Value.Should().Be(3);
|
||||
((long)instance.get_global_i64()).Should().Be(3);
|
||||
host.Float32Mut.Value.Should().Be(4);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(4);
|
||||
host.Float32.Value.Should().Be(5);
|
||||
((float)instance.get_global_f32()).Should().Be(5);
|
||||
host.Float64Mut.Value.Should().Be(6);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(6);
|
||||
host.Float64.Value.Should().Be(7);
|
||||
((double)instance.get_global_f64()).Should().Be(7);
|
||||
using dynamic instance = Fixture.Module.Instantiate(host);
|
||||
|
||||
host.Int32Mut.Value = 10;
|
||||
host.Int32Mut.Value.Should().Be(10);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(10);
|
||||
instance.set_global_i32_mut(11);
|
||||
host.Int32Mut.Value.Should().Be(11);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(11);
|
||||
host.Int32Mut.Value.Should().Be(0);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(0);
|
||||
host.Int32.Value.Should().Be(1);
|
||||
((int)instance.get_global_i32()).Should().Be(1);
|
||||
host.Int64Mut.Value.Should().Be(2);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(2);
|
||||
host.Int64.Value.Should().Be(3);
|
||||
((long)instance.get_global_i64()).Should().Be(3);
|
||||
host.Float32Mut.Value.Should().Be(4);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(4);
|
||||
host.Float32.Value.Should().Be(5);
|
||||
((float)instance.get_global_f32()).Should().Be(5);
|
||||
host.Float64Mut.Value.Should().Be(6);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(6);
|
||||
host.Float64.Value.Should().Be(7);
|
||||
((double)instance.get_global_f64()).Should().Be(7);
|
||||
|
||||
host.Int64Mut.Value = 12;
|
||||
host.Int64Mut.Value.Should().Be(12);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(12);
|
||||
instance.set_global_i64_mut(13);
|
||||
host.Int64Mut.Value.Should().Be(13);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(13);
|
||||
host.Int32Mut.Value = 10;
|
||||
host.Int32Mut.Value.Should().Be(10);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(10);
|
||||
instance.set_global_i32_mut(11);
|
||||
host.Int32Mut.Value.Should().Be(11);
|
||||
((int)instance.get_global_i32_mut()).Should().Be(11);
|
||||
|
||||
host.Float32Mut.Value = 14;
|
||||
host.Float32Mut.Value.Should().Be(14);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(14);
|
||||
instance.set_global_f32_mut(15);
|
||||
host.Float32Mut.Value.Should().Be(15);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(15);
|
||||
host.Int64Mut.Value = 12;
|
||||
host.Int64Mut.Value.Should().Be(12);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(12);
|
||||
instance.set_global_i64_mut(13);
|
||||
host.Int64Mut.Value.Should().Be(13);
|
||||
((long)instance.get_global_i64_mut()).Should().Be(13);
|
||||
|
||||
host.Float64Mut.Value = 16;
|
||||
host.Float64Mut.Value.Should().Be(16);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(16);
|
||||
instance.set_global_f64_mut(17);
|
||||
host.Float64Mut.Value.Should().Be(17);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(17);
|
||||
}
|
||||
host.Float32Mut.Value = 14;
|
||||
host.Float32Mut.Value.Should().Be(14);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(14);
|
||||
instance.set_global_f32_mut(15);
|
||||
host.Float32Mut.Value.Should().Be(15);
|
||||
((float)instance.get_global_f32_mut()).Should().Be(15);
|
||||
|
||||
host.Float64Mut.Value = 16;
|
||||
host.Float64Mut.Value.Should().Be(16);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(16);
|
||||
instance.set_global_f64_mut(17);
|
||||
host.Float64Mut.Value.Should().Be(17);
|
||||
((double)instance.get_global_f64_mut()).Should().Be(17);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,43 +45,42 @@ namespace Wasmtime.Tests
|
||||
public void ItCreatesExternsForTheMemories()
|
||||
{
|
||||
var host = new Host();
|
||||
using (var instance = Fixture.Module.Instantiate(host))
|
||||
{
|
||||
instance.Externs.Memories.Count.Should().Be(1);
|
||||
using var instance = Fixture.Module.Instantiate(host);
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
memory.ReadString(0, 11).Should().Be("Hello World");
|
||||
int written = memory.WriteString(0, "WebAssembly Rocks!");
|
||||
memory.ReadString(0, written).Should().Be("WebAssembly Rocks!");
|
||||
instance.Externs.Memories.Count.Should().Be(1);
|
||||
|
||||
memory.ReadByte(20).Should().Be(1);
|
||||
memory.WriteByte(20, 11);
|
||||
memory.ReadByte(20).Should().Be(11);
|
||||
var memory = instance.Externs.Memories[0];
|
||||
memory.ReadString(0, 11).Should().Be("Hello World");
|
||||
int written = memory.WriteString(0, "WebAssembly Rocks!");
|
||||
memory.ReadString(0, written).Should().Be("WebAssembly Rocks!");
|
||||
|
||||
memory.ReadInt16(21).Should().Be(2);
|
||||
memory.WriteInt16(21, 12);
|
||||
memory.ReadInt16(21).Should().Be(12);
|
||||
memory.ReadByte(20).Should().Be(1);
|
||||
memory.WriteByte(20, 11);
|
||||
memory.ReadByte(20).Should().Be(11);
|
||||
|
||||
memory.ReadInt32(23).Should().Be(3);
|
||||
memory.WriteInt32(23, 13);
|
||||
memory.ReadInt32(23).Should().Be(13);
|
||||
memory.ReadInt16(21).Should().Be(2);
|
||||
memory.WriteInt16(21, 12);
|
||||
memory.ReadInt16(21).Should().Be(12);
|
||||
|
||||
memory.ReadInt64(27).Should().Be(4);
|
||||
memory.WriteInt64(27, 14);
|
||||
memory.ReadInt64(27).Should().Be(14);
|
||||
memory.ReadInt32(23).Should().Be(3);
|
||||
memory.WriteInt32(23, 13);
|
||||
memory.ReadInt32(23).Should().Be(13);
|
||||
|
||||
memory.ReadSingle(35).Should().Be(5);
|
||||
memory.WriteSingle(35, 15);
|
||||
memory.ReadSingle(35).Should().Be(15);
|
||||
memory.ReadInt64(27).Should().Be(4);
|
||||
memory.WriteInt64(27, 14);
|
||||
memory.ReadInt64(27).Should().Be(14);
|
||||
|
||||
memory.ReadDouble(39).Should().Be(6);
|
||||
memory.WriteDouble(39, 16);
|
||||
memory.ReadDouble(39).Should().Be(16);
|
||||
memory.ReadSingle(35).Should().Be(5);
|
||||
memory.WriteSingle(35, 15);
|
||||
memory.ReadSingle(35).Should().Be(15);
|
||||
|
||||
memory.ReadIntPtr(48).Should().Be((IntPtr)7);
|
||||
memory.WriteIntPtr(48, (IntPtr)17);
|
||||
memory.ReadIntPtr(48).Should().Be((IntPtr)17);
|
||||
}
|
||||
memory.ReadDouble(39).Should().Be(6);
|
||||
memory.WriteDouble(39, 16);
|
||||
memory.ReadDouble(39).Should().Be(16);
|
||||
|
||||
memory.ReadIntPtr(48).Should().Be((IntPtr)7);
|
||||
memory.WriteIntPtr(48, (IntPtr)17);
|
||||
memory.ReadIntPtr(48).Should().Be((IntPtr)17);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetMemoryExports()
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithMissingImport()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new MissingImportsHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MissingImportsHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -85,7 +85,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithStaticField()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new MemoryIsStaticHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MemoryIsStaticHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -96,7 +96,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithNonReadOnlyField()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new MemoryIsNotReadOnlyHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new MemoryIsNotReadOnlyHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -107,7 +107,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWithInvalidType()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new NotAMemoryHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new NotAMemoryHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -118,7 +118,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenMemoryHasInvalidMinimum()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new InvalidMinimumHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new InvalidMinimumHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -129,7 +129,7 @@ namespace Wasmtime.Tests
|
||||
[Fact]
|
||||
public void ItFailsToInstantiateWhenMemoryHasInvalidMaximum()
|
||||
{
|
||||
Action action = () => { using (var instance = Fixture.Module.Instantiate(new InvalidMaximumHost())) { } };
|
||||
Action action = () => { using var instance = Fixture.Module.Instantiate(new InvalidMaximumHost()); };
|
||||
|
||||
action
|
||||
.Should()
|
||||
@@ -141,47 +141,46 @@ namespace Wasmtime.Tests
|
||||
public void ItBindsTheGlobalsCorrectly()
|
||||
{
|
||||
var host = new ValidHost();
|
||||
using (dynamic instance = Fixture.Module.Instantiate(host))
|
||||
{
|
||||
host.Mem.ReadString(0, 11).Should().Be("Hello World");
|
||||
int written = host.Mem.WriteString(0, "WebAssembly Rocks!");
|
||||
host.Mem.ReadString(0, written).Should().Be("WebAssembly Rocks!");
|
||||
using dynamic instance = Fixture.Module.Instantiate(host);
|
||||
|
||||
host.Mem.ReadByte(20).Should().Be(1);
|
||||
host.Mem.WriteByte(20, 11);
|
||||
host.Mem.ReadByte(20).Should().Be(11);
|
||||
((byte)instance.ReadByte()).Should().Be(11);
|
||||
host.Mem.ReadString(0, 11).Should().Be("Hello World");
|
||||
int written = host.Mem.WriteString(0, "WebAssembly Rocks!");
|
||||
host.Mem.ReadString(0, written).Should().Be("WebAssembly Rocks!");
|
||||
|
||||
host.Mem.ReadInt16(21).Should().Be(2);
|
||||
host.Mem.WriteInt16(21, 12);
|
||||
host.Mem.ReadInt16(21).Should().Be(12);
|
||||
((short)instance.ReadInt16()).Should().Be(12);
|
||||
host.Mem.ReadByte(20).Should().Be(1);
|
||||
host.Mem.WriteByte(20, 11);
|
||||
host.Mem.ReadByte(20).Should().Be(11);
|
||||
((byte)instance.ReadByte()).Should().Be(11);
|
||||
|
||||
host.Mem.ReadInt32(23).Should().Be(3);
|
||||
host.Mem.WriteInt32(23, 13);
|
||||
host.Mem.ReadInt32(23).Should().Be(13);
|
||||
((int)instance.ReadInt32()).Should().Be(13);
|
||||
host.Mem.ReadInt16(21).Should().Be(2);
|
||||
host.Mem.WriteInt16(21, 12);
|
||||
host.Mem.ReadInt16(21).Should().Be(12);
|
||||
((short)instance.ReadInt16()).Should().Be(12);
|
||||
|
||||
host.Mem.ReadInt64(27).Should().Be(4);
|
||||
host.Mem.WriteInt64(27, 14);
|
||||
host.Mem.ReadInt64(27).Should().Be(14);
|
||||
((long)instance.ReadInt64()).Should().Be(14);
|
||||
host.Mem.ReadInt32(23).Should().Be(3);
|
||||
host.Mem.WriteInt32(23, 13);
|
||||
host.Mem.ReadInt32(23).Should().Be(13);
|
||||
((int)instance.ReadInt32()).Should().Be(13);
|
||||
|
||||
host.Mem.ReadSingle(35).Should().Be(5);
|
||||
host.Mem.WriteSingle(35, 15);
|
||||
host.Mem.ReadSingle(35).Should().Be(15);
|
||||
((float)instance.ReadFloat32()).Should().Be(15);
|
||||
host.Mem.ReadInt64(27).Should().Be(4);
|
||||
host.Mem.WriteInt64(27, 14);
|
||||
host.Mem.ReadInt64(27).Should().Be(14);
|
||||
((long)instance.ReadInt64()).Should().Be(14);
|
||||
|
||||
host.Mem.ReadDouble(39).Should().Be(6);
|
||||
host.Mem.WriteDouble(39, 16);
|
||||
host.Mem.ReadDouble(39).Should().Be(16);
|
||||
((double)instance.ReadFloat64()).Should().Be(16);
|
||||
host.Mem.ReadSingle(35).Should().Be(5);
|
||||
host.Mem.WriteSingle(35, 15);
|
||||
host.Mem.ReadSingle(35).Should().Be(15);
|
||||
((float)instance.ReadFloat32()).Should().Be(15);
|
||||
|
||||
host.Mem.ReadIntPtr(48).Should().Be((IntPtr)7);
|
||||
host.Mem.WriteIntPtr(48, (IntPtr)17);
|
||||
host.Mem.ReadIntPtr(48).Should().Be((IntPtr)17);
|
||||
((IntPtr)instance.ReadIntPtr()).Should().Be((IntPtr)17);
|
||||
}
|
||||
host.Mem.ReadDouble(39).Should().Be(6);
|
||||
host.Mem.WriteDouble(39, 16);
|
||||
host.Mem.ReadDouble(39).Should().Be(16);
|
||||
((double)instance.ReadFloat64()).Should().Be(16);
|
||||
|
||||
host.Mem.ReadIntPtr(48).Should().Be((IntPtr)7);
|
||||
host.Mem.WriteIntPtr(48, (IntPtr)17);
|
||||
host.Mem.ReadIntPtr(48).Should().Be((IntPtr)17);
|
||||
((IntPtr)instance.ReadIntPtr()).Should().Be((IntPtr)17);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
crates/misc/dotnet/tests/Modules/Wasi.wasm
Normal file
BIN
crates/misc/dotnet/tests/Modules/Wasi.wasm
Normal file
Binary file not shown.
66
crates/misc/dotnet/tests/Modules/Wasi.wat
Normal file
66
crates/misc/dotnet/tests/Modules/Wasi.wat
Normal file
@@ -0,0 +1,66 @@
|
||||
(module
|
||||
(type $t0 (func (param i32 i32) (result i32)))
|
||||
(type $t1 (func (param i32 i32 i32 i32) (result i32)))
|
||||
(type $t2 (func (param i32) (result i32)))
|
||||
(type $t3 (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
|
||||
(import "wasi_snapshot_preview1" "environ_sizes_get" (func $wasi_snapshot_preview1.environ_sizes_get (type $t0)))
|
||||
(import "wasi_snapshot_preview1" "environ_get" (func $wasi_snapshot_preview1.environ_get (type $t0)))
|
||||
(import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi_snapshot_preview1.args_sizes_get (type $t0)))
|
||||
(import "wasi_snapshot_preview1" "args_get" (func $wasi_snapshot_preview1.args_get (type $t0)))
|
||||
(import "wasi_snapshot_preview1" "fd_write" (func $wasi_snapshot_preview1.fd_write (type $t1)))
|
||||
(import "wasi_snapshot_preview1" "fd_read" (func $wasi_snapshot_preview1.fd_read (type $t1)))
|
||||
(import "wasi_snapshot_preview1" "fd_close" (func $wasi_snapshot_preview1.fd_close (type $t2)))
|
||||
(import "wasi_snapshot_preview1" "path_open" (func $wasi_snapshot_preview1.path_open (type $t3)))
|
||||
(memory $memory 1)
|
||||
(export "memory" (memory 0))
|
||||
(func $call_environ_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
call $wasi_snapshot_preview1.environ_sizes_get)
|
||||
(func $call_environ_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
call $wasi_snapshot_preview1.environ_get)
|
||||
(func $call_args_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
call $wasi_snapshot_preview1.args_sizes_get)
|
||||
(func $call_args_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
call $wasi_snapshot_preview1.args_get)
|
||||
(func $call_fd_write (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
local.get $p2
|
||||
local.get $p3
|
||||
call $wasi_snapshot_preview1.fd_write)
|
||||
(func $call_fd_read (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
local.get $p2
|
||||
local.get $p3
|
||||
call $wasi_snapshot_preview1.fd_read)
|
||||
(func $call_fd_close (type $t2) (param $p0 i32) (result i32)
|
||||
local.get $p0
|
||||
call $wasi_snapshot_preview1.fd_close)
|
||||
(func $call_path_open (type $t3) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (param $p4 i32) (param $p5 i64) (param $p6 i64) (param $p7 i32) (param $p8 i32) (result i32)
|
||||
local.get $p0
|
||||
local.get $p1
|
||||
local.get $p2
|
||||
local.get $p3
|
||||
local.get $p4
|
||||
local.get $p5
|
||||
local.get $p6
|
||||
local.get $p7
|
||||
local.get $p8
|
||||
call $wasi_snapshot_preview1.path_open)
|
||||
(export "call_environ_sizes_get" (func $call_environ_sizes_get))
|
||||
(export "call_environ_get" (func $call_environ_get))
|
||||
(export "call_args_sizes_get" (func $call_args_sizes_get))
|
||||
(export "call_args_get" (func $call_args_get))
|
||||
(export "call_fd_write" (func $call_fd_write))
|
||||
(export "call_fd_read" (func $call_fd_read))
|
||||
(export "call_fd_close" (func $call_fd_close))
|
||||
(export "call_path_open" (func $call_path_open))
|
||||
)
|
||||
24
crates/misc/dotnet/tests/TempFile.cs
Normal file
24
crates/misc/dotnet/tests/TempFile.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Wasmtime.Tests
|
||||
{
|
||||
internal class TempFile : IDisposable
|
||||
{
|
||||
public TempFile()
|
||||
{
|
||||
Path = System.IO.Path.GetTempFileName();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Path != null)
|
||||
{
|
||||
File.Delete(Path);
|
||||
Path = null;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path { get; private set; }
|
||||
}
|
||||
}
|
||||
246
crates/misc/dotnet/tests/WasiTests.cs
Normal file
246
crates/misc/dotnet/tests/WasiTests.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Wasmtime.Tests
|
||||
{
|
||||
public class WasiFixture : ModuleFixture
|
||||
{
|
||||
protected override string ModuleFileName => "Wasi.wasm";
|
||||
}
|
||||
|
||||
public class WasiTests : IClassFixture<WasiFixture>
|
||||
{
|
||||
public WasiTests(WasiFixture fixture)
|
||||
{
|
||||
Fixture = fixture;
|
||||
}
|
||||
|
||||
private WasiFixture Fixture { get; set; }
|
||||
|
||||
[Fact]
|
||||
public void ItHasNoEnvironmentByDefault()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store));
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
|
||||
Assert.Equal(0, memory.ReadInt32(0));
|
||||
Assert.Equal(0, memory.ReadInt32(4));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItHasSpecifiedEnvironment()
|
||||
{
|
||||
var env = new Dictionary<string, string>() {
|
||||
{"FOO", "BAR"},
|
||||
{"WASM", "IS"},
|
||||
{"VERY", "COOL"},
|
||||
};
|
||||
|
||||
var wasi = new WasiBuilder()
|
||||
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)))
|
||||
.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
|
||||
Assert.Equal(env.Count, memory.ReadInt32(0));
|
||||
Assert.Equal(env.Sum(kvp => kvp.Key.Length + kvp.Value.Length + 2), memory.ReadInt32(4));
|
||||
Assert.Equal(0, inst.call_environ_get(0, 4 * env.Count));
|
||||
|
||||
for (int i = 0; i < env.Count; ++i)
|
||||
{
|
||||
var kvp = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4)).Split("=");
|
||||
Assert.Equal(env[kvp[0]], kvp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItInheritsEnvironment()
|
||||
{
|
||||
var wasi = new WasiBuilder()
|
||||
.WithInheritedEnvironment()
|
||||
.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
|
||||
Assert.Equal(Environment.GetEnvironmentVariables().Keys.Count, memory.ReadInt32(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItHasNoArgumentsByDefault()
|
||||
{
|
||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store));
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
|
||||
Assert.Equal(0, memory.ReadInt32(0));
|
||||
Assert.Equal(0, memory.ReadInt32(4));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItHasSpecifiedArguments()
|
||||
{
|
||||
var args = new List<string>() {
|
||||
"WASM",
|
||||
"IS",
|
||||
"VERY",
|
||||
"COOL"
|
||||
};
|
||||
|
||||
var wasi = new WasiBuilder()
|
||||
.WithArgs(args)
|
||||
.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
|
||||
Assert.Equal(args.Count, memory.ReadInt32(0));
|
||||
Assert.Equal(args.Sum(a => a.Length + 1), memory.ReadInt32(4));
|
||||
Assert.Equal(0, inst.call_args_get(0, 4 * args.Count));
|
||||
|
||||
for (int i = 0; i < args.Count; ++i)
|
||||
{
|
||||
var arg = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4));
|
||||
Assert.Equal(args[i], arg);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItInheritsArguments()
|
||||
{
|
||||
var wasi = new WasiBuilder()
|
||||
.WithInheritedArgs()
|
||||
.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
|
||||
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
|
||||
Assert.Equal(Environment.GetCommandLineArgs().Length, memory.ReadInt32(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItSetsStdIn()
|
||||
{
|
||||
const string MESSAGE = "WASM IS VERY COOL";
|
||||
|
||||
using var file = new TempFile();
|
||||
File.WriteAllText(file.Path, MESSAGE);
|
||||
|
||||
var wasi = new WasiBuilder()
|
||||
.WithStandardInput(file.Path)
|
||||
.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
memory.WriteInt32(0, 8);
|
||||
memory.WriteInt32(4, MESSAGE.Length);
|
||||
|
||||
Assert.Equal(0, inst.call_fd_read(0, 0, 1, 32));
|
||||
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
|
||||
Assert.Equal(MESSAGE, memory.ReadString(8, MESSAGE.Length));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
public void ItSetsStdOutAndStdErr(int fd)
|
||||
{
|
||||
const string MESSAGE = "WASM IS VERY COOL";
|
||||
|
||||
using var file = new TempFile();
|
||||
|
||||
var builder = new WasiBuilder();
|
||||
if (fd == 1)
|
||||
{
|
||||
builder.WithStandardOutput(file.Path);
|
||||
}
|
||||
else if (fd == 2)
|
||||
{
|
||||
builder.WithStandardError(file.Path);
|
||||
}
|
||||
|
||||
var wasi = builder.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
memory.WriteInt32(0, 8);
|
||||
memory.WriteInt32(4, MESSAGE.Length);
|
||||
memory.WriteString(8, MESSAGE);
|
||||
|
||||
Assert.Equal(0, inst.call_fd_write(fd, 0, 1, 32));
|
||||
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
|
||||
Assert.Equal(0, inst.call_fd_close(fd));
|
||||
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItSetsPreopenDirectories()
|
||||
{
|
||||
const string MESSAGE = "WASM IS VERY COOL";
|
||||
|
||||
using var file = new TempFile();
|
||||
|
||||
var wasi = new WasiBuilder()
|
||||
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo")
|
||||
.Build(Fixture.Module.Store);
|
||||
|
||||
using var instance = Fixture.Module.Instantiate(wasi);
|
||||
dynamic inst = instance;
|
||||
|
||||
var memory = instance.Externs.Memories[0];
|
||||
var fileName = Path.GetFileName(file.Path);
|
||||
memory.WriteString(0, fileName);
|
||||
|
||||
Assert.Equal(0, inst.call_path_open(
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
fileName.Length,
|
||||
0,
|
||||
0x40 /* RIGHTS_FD_WRITE */,
|
||||
0,
|
||||
0,
|
||||
64
|
||||
)
|
||||
);
|
||||
|
||||
var fileFd = (int) memory.ReadInt32(64);
|
||||
Assert.True(fileFd > 3);
|
||||
|
||||
memory.WriteInt32(0, 8);
|
||||
memory.WriteInt32(4, MESSAGE.Length);
|
||||
memory.WriteString(8, MESSAGE);
|
||||
|
||||
Assert.Equal(0, inst.call_fd_write(fileFd, 0, 1, 64));
|
||||
Assert.Equal(MESSAGE.Length, memory.ReadInt32(64));
|
||||
Assert.Equal(0, inst.call_fd_close(fileFd));
|
||||
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,12 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
||||
|
||||
// Create our wasi context with pretty standard arguments/inheritance/etc.
|
||||
// Additionally register andy preopened directories if we have them.
|
||||
let mut builder = wasi_common::WasiCtxBuilder::new()
|
||||
.arg(bin_name)
|
||||
.arg(".")
|
||||
.inherit_stdio();
|
||||
let mut builder = wasi_common::WasiCtxBuilder::new();
|
||||
|
||||
builder.arg(bin_name).arg(".").inherit_stdio();
|
||||
|
||||
for (dir, file) in get_preopens(workspace)? {
|
||||
builder = builder.preopened_dir(file, dir);
|
||||
builder.preopened_dir(file, dir);
|
||||
}
|
||||
|
||||
// The nonstandard thing we do with `WasiCtxBuilder` is to ensure that
|
||||
@@ -32,7 +32,7 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
||||
// where `stdin` is never ready to be read. In some CI systems, however,
|
||||
// stdin is closed which causes tests to fail.
|
||||
let (reader, _writer) = os_pipe::pipe()?;
|
||||
builder = builder.stdin(reader_to_file(reader));
|
||||
builder.stdin(reader_to_file(reader));
|
||||
let snapshot1 = wasmtime_wasi::Wasi::new(&store, builder.build()?);
|
||||
let module = Module::new(&store, &data).context("failed to create wasm module")?;
|
||||
let imports = module
|
||||
|
||||
@@ -60,38 +60,38 @@ impl PendingCString {
|
||||
|
||||
/// A builder allowing customizable construction of `WasiCtx` instances.
|
||||
pub struct WasiCtxBuilder {
|
||||
fds: HashMap<wasi::__wasi_fd_t, PendingFdEntry>,
|
||||
preopens: Vec<(PathBuf, File)>,
|
||||
args: Vec<PendingCString>,
|
||||
env: HashMap<PendingCString, PendingCString>,
|
||||
fds: Option<HashMap<wasi::__wasi_fd_t, PendingFdEntry>>,
|
||||
preopens: Option<Vec<(PathBuf, File)>>,
|
||||
args: Option<Vec<PendingCString>>,
|
||||
env: Option<HashMap<PendingCString, PendingCString>>,
|
||||
}
|
||||
|
||||
impl WasiCtxBuilder {
|
||||
/// Builder for a new `WasiCtx`.
|
||||
pub fn new() -> Self {
|
||||
let mut builder = Self {
|
||||
fds: HashMap::new(),
|
||||
preopens: Vec::new(),
|
||||
args: vec![],
|
||||
env: HashMap::new(),
|
||||
};
|
||||
let mut fds = HashMap::new();
|
||||
|
||||
builder.fds.insert(0, PendingFdEntry::Thunk(FdEntry::null));
|
||||
builder.fds.insert(1, PendingFdEntry::Thunk(FdEntry::null));
|
||||
builder.fds.insert(2, PendingFdEntry::Thunk(FdEntry::null));
|
||||
fds.insert(0, PendingFdEntry::Thunk(FdEntry::null));
|
||||
fds.insert(1, PendingFdEntry::Thunk(FdEntry::null));
|
||||
fds.insert(2, PendingFdEntry::Thunk(FdEntry::null));
|
||||
|
||||
builder
|
||||
Self {
|
||||
fds: Some(fds),
|
||||
preopens: Some(Vec::new()),
|
||||
args: Some(Vec::new()),
|
||||
env: Some(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add arguments to the command-line arguments list.
|
||||
///
|
||||
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail
|
||||
/// with `Error::EILSEQ`.
|
||||
pub fn args<S: AsRef<[u8]>>(mut self, args: impl IntoIterator<Item = S>) -> Self {
|
||||
self.args = args
|
||||
.into_iter()
|
||||
.map(|arg| arg.as_ref().to_vec().into())
|
||||
.collect();
|
||||
pub fn args<S: AsRef<[u8]>>(&mut self, args: impl IntoIterator<Item = S>) -> &mut Self {
|
||||
self.args
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.extend(args.into_iter().map(|a| a.as_ref().to_vec().into()));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -99,8 +99,11 @@ impl WasiCtxBuilder {
|
||||
///
|
||||
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail
|
||||
/// with `Error::EILSEQ`.
|
||||
pub fn arg<S: AsRef<[u8]>>(mut self, arg: S) -> Self {
|
||||
self.args.push(arg.as_ref().to_vec().into());
|
||||
pub fn arg<S: AsRef<[u8]>>(&mut self, arg: S) -> &mut Self {
|
||||
self.args
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push(arg.as_ref().to_vec().into());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -108,19 +111,46 @@ impl WasiCtxBuilder {
|
||||
///
|
||||
/// If any arguments from the host process contain invalid UTF-8, `WasiCtxBuilder::build()` will
|
||||
/// fail with `Error::EILSEQ`.
|
||||
pub fn inherit_args(mut self) -> Self {
|
||||
self.args = env::args_os().map(PendingCString::OsString).collect();
|
||||
pub fn inherit_args(&mut self) -> &mut Self {
|
||||
let args = self.args.as_mut().unwrap();
|
||||
args.clear();
|
||||
args.extend(env::args_os().map(PendingCString::OsString));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdin from the host process.
|
||||
pub fn inherit_stdin(&mut self) -> &mut Self {
|
||||
self.fds
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(0, PendingFdEntry::Thunk(FdEntry::duplicate_stdin));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdout from the host process.
|
||||
pub fn inherit_stdout(&mut self) -> &mut Self {
|
||||
self.fds
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(1, PendingFdEntry::Thunk(FdEntry::duplicate_stdout));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdout from the host process.
|
||||
pub fn inherit_stderr(&mut self) -> &mut Self {
|
||||
self.fds
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.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
|
||||
.insert(0, PendingFdEntry::Thunk(FdEntry::duplicate_stdin));
|
||||
self.fds
|
||||
.insert(1, PendingFdEntry::Thunk(FdEntry::duplicate_stdout));
|
||||
self.fds
|
||||
.insert(2, PendingFdEntry::Thunk(FdEntry::duplicate_stderr));
|
||||
pub fn inherit_stdio(&mut self) -> &mut Self {
|
||||
let fds = self.fds.as_mut().unwrap();
|
||||
fds.insert(0, PendingFdEntry::Thunk(FdEntry::duplicate_stdin));
|
||||
fds.insert(1, PendingFdEntry::Thunk(FdEntry::duplicate_stdout));
|
||||
fds.insert(2, PendingFdEntry::Thunk(FdEntry::duplicate_stderr));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -129,10 +159,10 @@ impl WasiCtxBuilder {
|
||||
/// If any environment variables from the host process contain invalid Unicode (UTF-16 for
|
||||
/// Windows, UTF-8 for other platforms), `WasiCtxBuilder::build()` will fail with
|
||||
/// `Error::EILSEQ`.
|
||||
pub fn inherit_env(mut self) -> Self {
|
||||
self.env = std::env::vars_os()
|
||||
.map(|(k, v)| (k.into(), v.into()))
|
||||
.collect();
|
||||
pub fn inherit_env(&mut self) -> &mut Self {
|
||||
let env = self.env.as_mut().unwrap();
|
||||
env.clear();
|
||||
env.extend(std::env::vars_os().map(|(k, v)| (k.into(), v.into())));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -140,8 +170,10 @@ impl WasiCtxBuilder {
|
||||
///
|
||||
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
||||
/// `WasiCtxBuilder::build()` will fail with `Error::EILSEQ`.
|
||||
pub fn env<S: AsRef<[u8]>>(mut self, k: S, v: S) -> Self {
|
||||
pub fn env<S: AsRef<[u8]>>(&mut self, k: S, v: S) -> &mut Self {
|
||||
self.env
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(k.as_ref().to_vec().into(), v.as_ref().to_vec().into());
|
||||
self
|
||||
}
|
||||
@@ -151,40 +183,49 @@ impl WasiCtxBuilder {
|
||||
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
||||
/// `WasiCtxBuilder::build()` will fail with `Error::EILSEQ`.
|
||||
pub fn envs<S: AsRef<[u8]>, T: Borrow<(S, S)>>(
|
||||
mut self,
|
||||
&mut self,
|
||||
envs: impl IntoIterator<Item = T>,
|
||||
) -> Self {
|
||||
self.env = envs
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
let (k, v) = t.borrow();
|
||||
(k.as_ref().to_vec().into(), v.as_ref().to_vec().into())
|
||||
})
|
||||
.collect();
|
||||
) -> &mut Self {
|
||||
self.env.as_mut().unwrap().extend(envs.into_iter().map(|t| {
|
||||
let (k, v) = t.borrow();
|
||||
(k.as_ref().to_vec().into(), v.as_ref().to_vec().into())
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a File to use as stdin
|
||||
pub fn stdin(mut self, file: File) -> Self {
|
||||
self.fds.insert(0, PendingFdEntry::File(file));
|
||||
pub fn stdin(&mut self, file: File) -> &mut Self {
|
||||
self.fds
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(0, PendingFdEntry::File(file));
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a File to use as stdout
|
||||
pub fn stdout(mut self, file: File) -> Self {
|
||||
self.fds.insert(1, PendingFdEntry::File(file));
|
||||
pub fn stdout(&mut self, file: File) -> &mut Self {
|
||||
self.fds
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(1, PendingFdEntry::File(file));
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a File to use as stderr
|
||||
pub fn stderr(mut self, file: File) -> Self {
|
||||
self.fds.insert(2, PendingFdEntry::File(file));
|
||||
pub fn stderr(&mut self, file: File) -> &mut Self {
|
||||
self.fds
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(2, PendingFdEntry::File(file));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a preopened directory.
|
||||
pub fn preopened_dir<P: AsRef<Path>>(mut self, dir: File, guest_path: P) -> Self {
|
||||
self.preopens.push((guest_path.as_ref().to_owned(), dir));
|
||||
pub fn preopened_dir<P: AsRef<Path>>(&mut self, dir: File, guest_path: P) -> &mut Self {
|
||||
self.preopens
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push((guest_path.as_ref().to_owned(), dir));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -192,17 +233,21 @@ impl WasiCtxBuilder {
|
||||
///
|
||||
/// If any of the arguments or environment variables in this builder cannot be converted into
|
||||
/// `CString`s, either due to NUL bytes or Unicode conversions, this returns `Error::EILSEQ`.
|
||||
pub fn build(self) -> Result<WasiCtx> {
|
||||
pub fn build(&mut self) -> Result<WasiCtx> {
|
||||
// Process arguments and environment variables into `CString`s, failing quickly if they
|
||||
// contain any NUL bytes, or if conversion from `OsString` fails.
|
||||
let args = self
|
||||
.args
|
||||
.take()
|
||||
.ok_or(Error::EINVAL)?
|
||||
.into_iter()
|
||||
.map(|arg| arg.into_utf8_cstring())
|
||||
.collect::<Result<Vec<CString>>>()?;
|
||||
|
||||
let env = self
|
||||
.env
|
||||
.take()
|
||||
.ok_or(Error::EINVAL)?
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
k.into_string().and_then(|mut pair| {
|
||||
@@ -219,7 +264,7 @@ impl WasiCtxBuilder {
|
||||
|
||||
let mut fds: HashMap<wasi::__wasi_fd_t, FdEntry> = HashMap::new();
|
||||
// Populate the non-preopen fds.
|
||||
for (fd, pending) in self.fds {
|
||||
for (fd, pending) in self.fds.take().ok_or(Error::EINVAL)? {
|
||||
log::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
|
||||
match pending {
|
||||
PendingFdEntry::Thunk(f) => {
|
||||
@@ -234,7 +279,7 @@ impl WasiCtxBuilder {
|
||||
// so we start from there. This variable is initially 2, though, because the loop
|
||||
// immediately does the increment and check for overflow.
|
||||
let mut preopen_fd: wasi::__wasi_fd_t = 2;
|
||||
for (guest_path, dir) in self.preopens {
|
||||
for (guest_path, dir) in self.preopens.take().ok_or(Error::EINVAL)? {
|
||||
// We do the increment at the beginning of the loop body, so that we don't overflow
|
||||
// unnecessarily if we have exactly the maximum number of file descriptors.
|
||||
preopen_fd = preopen_fd.checked_add(1).ok_or(Error::ENFILE)?;
|
||||
|
||||
Reference in New Issue
Block a user