Skip to content

Commit

Permalink
Merge #595
Browse files Browse the repository at this point in the history
595: Add useful functions for external use of WASI filesystem r=MarkMcCaskey a=MarkMcCaskey

part of #583 

Co-authored-by: Mark McCaskey <mark@wasmer.io>
Co-authored-by: Mark McCaskey <markmccaskey@users.noreply.github.com>
  • Loading branch information
3 people committed Jul 31, 2019
2 parents 99ecfaa + b407633 commit ae05d6f
Show file tree
Hide file tree
Showing 8 changed files with 542 additions and 204 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.

## **[Unreleased]**
- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases
- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows
- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test.
- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends.
Expand Down
Binary file modified examples/plugin-for-example.wasm
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/plugin-for-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allo

Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24).

We call the main function to initialize WASI's libpreopen internal datastructures and have the module call back into the host to set swap out the modules implementation of stdout. The host then provides a wrapper around stdout, allowing the guest's writes to stdout to be formatted in a host-appropriate manner.

[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
5 changes: 4 additions & 1 deletion examples/plugin-for-example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern "C" {
fn it_works() -> i32;
fn initialize();
}

#[no_mangle]
Expand All @@ -9,4 +10,6 @@ pub fn plugin_entrypoint(n: i32) -> i32 {
result + n
}

pub fn main() {}
pub fn main() {
unsafe { initialize() };
}
110 changes: 109 additions & 1 deletion examples/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use wasmer_runtime::{func, imports, instantiate};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::generate_import_object;
use wasmer_wasi::{
generate_import_object,
state::{self, WasiFile},
types,
};

static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";

Expand All @@ -9,6 +13,107 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
5
}

#[derive(Debug)]
pub struct LoggingWrapper {
pub wasm_module_name: String,
}

// std io trait boiler plate so we can implement WasiFile
// LoggingWrapper is a write-only type so we just want to immediately
// fail when reading or Seeking
impl std::io::Read for LoggingWrapper {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
}
impl std::io::Seek for LoggingWrapper {
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek logging wrapper",
))
}
}
impl std::io::Write for LoggingWrapper {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
std::io::stdout().flush()
}
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_all(buf)
}
fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_fmt(fmt)
}
}

// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
impl WasiFile for LoggingWrapper {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}

/// Called by the program when it wants to set itself up
fn initialize(ctx: &mut Ctx) {
let state = state::get_wasi_state(ctx);
let wasi_file_inner = LoggingWrapper {
wasm_module_name: "example module name".to_string(),
};
// swap stdout with our new wasifile
let _old_stdout = state
.fs
.swap_file(types::__WASI_STDOUT_FILENO, Box::new(wasi_file_inner))
.unwrap();
}

fn main() {
// Load the plugin data
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
Expand All @@ -22,6 +127,7 @@ fn main() {
let custom_imports = imports! {
"env" => {
"it_works" => func!(it_works),
"initialize" => func!(initialize),
},
};
// The WASI imports object contains all required import functions for a WASI module to run.
Expand All @@ -30,6 +136,8 @@ fn main() {
let instance =
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");

let main = instance.func::<(), ()>("_start").unwrap();
main.call().expect("Could not initialize");
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument
Expand Down
3 changes: 2 additions & 1 deletion lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ extern crate winapi;
#[macro_use]
mod macros;
mod ptr;
mod state;
pub mod state;
mod syscalls;
mod utils;

use self::state::{WasiFs, WasiState};
pub use self::syscalls::types;
use self::syscalls::*;

use std::ffi::c_void;
Expand Down
Loading

0 comments on commit ae05d6f

Please sign in to comment.