Skip to content

Commit

Permalink
Merge pull request #169 from rvolosatovs/feat/preview1
Browse files Browse the repository at this point in the history
feat(preview1): implement stateless host WASI calls
  • Loading branch information
pchickey authored May 11, 2023
2 parents 54e6405 + e944531 commit f70eb99
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 17 deletions.
9 changes: 8 additions & 1 deletion host/tests/wasi_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ async fn instantiate(
Ok((store, command))
}
async fn run_with_temp_dir(component: &str) {
let mut builder = WasiCtxBuilder::new().push_env("NO_FDFLAGS_SYNC_SUPPORT", "1");
let mut builder = WasiCtxBuilder::new()
.push_env("NO_FDFLAGS_SYNC_SUPPORT", "1")
.push_env("TEST", "1");

if cfg!(windows) {
builder = builder
Expand Down Expand Up @@ -318,3 +320,8 @@ async fn unlink_file_trailing_slashes() {
async fn dir_fd_op_failures() {
run_with_temp_dir("dir_fd_op_failures").await
}

#[test_log::test(tokio::test)]
async fn environment() {
run_with_temp_dir("environment").await
}
10 changes: 6 additions & 4 deletions host/tests/wasi_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ async fn instantiate(module: Module, ctx: CommandCtx) -> Result<(Store<CommandCt
Ok((store, instance))
}
async fn run_with_temp_dir(module: &str) {
let mut builder = WasiCtxBuilder::new();
let mut builder = WasiCtxBuilder::new().push_env("TEST", "1");

if cfg!(windows) {
builder = builder
Expand Down Expand Up @@ -110,13 +110,11 @@ async fn run_with_temp_dir(module: &str) {
}

#[test_log::test(tokio::test)]
#[should_panic]
async fn big_random_buf() {
run_with_temp_dir("big_random_buf").await
}

#[test_log::test(tokio::test)]
#[should_panic]
async fn clock_time_get() {
run_with_temp_dir("clock_time_get").await
}
Expand Down Expand Up @@ -329,7 +327,6 @@ async fn renumber() {
}

#[test_log::test(tokio::test)]
#[should_panic]
async fn sched_yield() {
run_with_temp_dir("sched_yield").await
}
Expand Down Expand Up @@ -369,3 +366,8 @@ async fn unlink_file_trailing_slashes() {
async fn dir_fd_op_failures() {
run_with_temp_dir("dir_fd_op_failures").await
}

#[test_log::test(tokio::test)]
async fn environment() {
run_with_temp_dir("environment").await
}
6 changes: 6 additions & 0 deletions test-programs/wasi-tests/src/bin/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use std::env;

fn main() {
assert_eq!(env::args().collect::<Vec<_>>(), ["program", "/foo"]);
assert_eq!(env::var("TEST").as_deref(), Ok("1"));
}
179 changes: 167 additions & 12 deletions wasi-common/src/preview1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
#![allow(unused_variables)]

use crate::wasi;

use core::borrow::Borrow;

use anyhow::{anyhow, Context};
use wiggle::GuestPtr;

pub struct WasiPreview1Adapter {/* all members private and only used inside this module. also, this struct should be Send. */}
Expand All @@ -10,7 +14,7 @@ impl WasiPreview1Adapter {
// no parameters: anything it needs from the preview 2 implementation
// should be retrieved lazily.
pub fn new() -> Self {
todo!()
Self {}
}
}

Expand Down Expand Up @@ -56,6 +60,46 @@ impl wiggle::GuestErrorType for types::Errno {
}
}

impl TryFrom<wasi::wall_clock::Datetime> for types::Timestamp {
type Error = types::Errno;

fn try_from(
wasi::wall_clock::Datetime {
seconds,
nanoseconds,
}: wasi::wall_clock::Datetime,
) -> Result<Self, Self::Error> {
types::Timestamp::from(seconds)
.checked_mul(1_000_000_000)
.and_then(|ns| ns.checked_add(nanoseconds.into()))
.ok_or(types::Errno::Overflow)
}
}

type ErrnoResult<T> = Result<T, types::Errno>;

fn write_bytes<'a>(
ptr: impl Borrow<GuestPtr<'a, u8>>,
buf: impl AsRef<[u8]>,
) -> ErrnoResult<GuestPtr<'a, u8>> {
// NOTE: legacy implementation always returns Inval errno

let buf = buf.as_ref();
let len = buf.len().try_into().or(Err(types::Errno::Inval))?;

let ptr = ptr.borrow();
ptr.as_array(len)
.copy_from_slice(buf)
.or(Err(types::Errno::Inval))?;
ptr.add(len).or(Err(types::Errno::Inval))
}

fn write_byte<'a>(ptr: impl Borrow<GuestPtr<'a, u8>>, byte: u8) -> ErrnoResult<GuestPtr<'a, u8>> {
let ptr = ptr.borrow();
ptr.write(byte).or(Err(types::Errno::Inval))?;
ptr.add(1).or(Err(types::Errno::Inval))
}

// Implement the WasiSnapshotPreview1 trait using only the traits that are
// required for T, i.e., in terms of the preview 2 wit interface, and state
// stored in the WasiPreview1Adapter struct.
Expand All @@ -78,38 +122,135 @@ impl<
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
argv_buf: &GuestPtr<'b, u8>,
) -> Result<(), types::Error> {
todo!()
self.get_arguments()
.await
.context("failed to call `get-arguments`")
.map_err(types::Error::trap)?
.into_iter()
.try_fold(
(*argv, *argv_buf),
|(argv, argv_buf), arg| -> ErrnoResult<_> {
// NOTE: legacy implementation always returns Inval errno

argv.write(argv_buf).map_err(|_| types::Errno::Inval)?;
let argv = argv.add(1).map_err(|_| types::Errno::Inval)?;

let argv_buf = write_bytes(argv_buf, arg)?;
let argv_buf = write_byte(argv_buf, 0)?;

Ok((argv, argv_buf))
},
)?;
Ok(())
}

async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), types::Error> {
todo!()
let args = self
.get_arguments()
.await
.context("failed to call `get-arguments`")
.map_err(types::Error::trap)?;
let num = args.len().try_into().map_err(|_| types::Errno::Overflow)?;
let len = args
.iter()
.map(|buf| buf.len() + 1) // Each argument is expected to be `\0` terminated.
.sum::<usize>()
.try_into()
.map_err(|_| types::Errno::Overflow)?;
Ok((num, len))
}

async fn environ_get<'b>(
&mut self,
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
environ_buf: &GuestPtr<'b, u8>,
) -> Result<(), types::Error> {
todo!()
self.get_environment()
.await
.context("failed to call `get-environment`")
.map_err(types::Error::trap)?
.into_iter()
.try_fold(
(*environ, *environ_buf),
|(environ, environ_buf), (k, v)| -> ErrnoResult<_> {
// NOTE: legacy implementation always returns Inval errno

environ
.write(environ_buf)
.map_err(|_| types::Errno::Inval)?;
let environ = environ.add(1).map_err(|_| types::Errno::Inval)?;

let environ_buf = write_bytes(environ_buf, k)?;
let environ_buf = write_byte(environ_buf, b'=')?;
let environ_buf = write_bytes(environ_buf, v)?;
let environ_buf = write_byte(environ_buf, 0)?;

Ok((environ, environ_buf))
},
)?;
Ok(())
}

async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), types::Error> {
todo!()
let environ = self
.get_environment()
.await
.context("failed to call `get-environment`")
.map_err(types::Error::trap)?;
let num = environ
.len()
.try_into()
.map_err(|_| types::Errno::Overflow)?;
let len = environ
.iter()
.map(|(k, v)| k.len() + 1 + v.len() + 1) // Key/value pairs are expected to be joined with `=`s, and terminated with `\0`s.
.sum::<usize>()
.try_into()
.map_err(|_| types::Errno::Overflow)?;
Ok((num, len))
}

async fn clock_res_get(
&mut self,
id: types::Clockid,
) -> Result<types::Timestamp, types::Error> {
todo!()
let res = match id {
types::Clockid::Realtime => wasi::wall_clock::Host::resolution(self)
.await
.context("failed to call `wall_clock::resolution`")
.map_err(types::Error::trap)?
.try_into()?,
types::Clockid::Monotonic => wasi::monotonic_clock::Host::resolution(self)
.await
.context("failed to call `monotonic_clock::resolution`")
.map_err(types::Error::trap)?,
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
return Err(types::Errno::Badf.into())
}
};
Ok(res)
}

async fn clock_time_get(
&mut self,
id: types::Clockid,
precision: types::Timestamp,
_precision: types::Timestamp,
) -> Result<types::Timestamp, types::Error> {
todo!()
let now = match id {
types::Clockid::Realtime => wasi::wall_clock::Host::now(self)
.await
.context("failed to call `wall_clock::now`")
.map_err(types::Error::trap)?
.try_into()?,
types::Clockid::Monotonic => wasi::monotonic_clock::Host::now(self)
.await
.context("failed to call `monotonic_clock::now`")
.map_err(types::Error::trap)?,
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
return Err(types::Errno::Badf.into())
}
};
Ok(now)
}

async fn fd_advise(
Expand Down Expand Up @@ -367,23 +508,37 @@ impl<
}

async fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error {
todo!()
let status = match status {
0 => Ok(()),
_ => Err(()),
};
match self.exit(status).await {
Err(e) => e,
Ok(()) => anyhow!("`exit` did not return an error"),
}
}

async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), types::Error> {
todo!()
Err(types::Errno::Notsup.into())
}

async fn sched_yield(&mut self) -> Result<(), types::Error> {
todo!()
// TODO: This is not yet covered in Preview2.
Ok(())
}

async fn random_get<'a>(
&mut self,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
) -> Result<(), types::Error> {
todo!()
let rand = self
.get_random_bytes(buf_len.into())
.await
.context("failed to call `get-random-bytes`")
.map_err(types::Error::trap)?;
write_bytes(buf, rand)?;
Ok(())
}

async fn sock_accept(
Expand Down

0 comments on commit f70eb99

Please sign in to comment.