Skip to content

Commit 1fe71a5

Browse files
committed
Use wasmtime-wasi APIs instead of wasi-common
1 parent b4735af commit 1fe71a5

9 files changed

Lines changed: 155 additions & 148 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ ruby-api = []
1919
winch = ["wasmtime/winch"]
2020

2121
[dependencies]
22+
async-trait = "*"
23+
bytes = "*"
2224
lazy_static = "1.5.0"
2325
magnus = { version = "0.7", features = ["rb-sys"] }
2426
rb-sys = { version = "*", default-features = false, features = [
@@ -40,7 +42,7 @@ async-timer = { version = "1.0.0-beta.15", features = [
4042
], optional = true }
4143
static_assertions = "1.1.0"
4244
wasmtime-environ = "=31.0.0"
43-
deterministic-wasi-ctx = "=0.1.34"
45+
deterministic-wasi-ctx = "=1.0.0"
4446

4547
[build-dependencies]
4648
rb-sys-env = "0.2.2"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use std::sync::Arc;
2+
3+
use wasmtime_wasi::{FileInputStream, StdinStream};
4+
5+
pub struct FileStdinStream {
6+
file: Arc<File>,
7+
}
8+
9+
impl FileStdinStream {
10+
pub fn new(file: File) -> Self {
11+
Self {
12+
file: Arc::new(file),
13+
}
14+
}
15+
}
16+
17+
impl StdinStream for FileStdinStream {
18+
fn stream(&self) -> Box<dyn wasmtime_wasi::InputStream> {
19+
Box::new(FileInputStream::new(Arc::clone(&self.file), 0))
20+
}
21+
22+
fn isatty(&self) -> bool {
23+
false
24+
}
25+
}

ext/src/helpers/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
mod file_stdin_stream;
12
mod macros;
23
mod nogvl;
34
mod output_limited_buffer;
45
mod static_id;
56
mod symbol_enum;
67
mod tmplock;
78

9+
pub use file_stdin_stream::FileStdinStream;
810
pub use nogvl::nogvl;
9-
pub use output_limited_buffer::OutputLimitedBuffer;
11+
pub use output_limited_buffer::{OutputLimitedBuffer, WritePipe};
1012
pub use static_id::StaticId;
1113
pub use symbol_enum::SymbolEnum;
1214
pub use tmplock::Tmplock;
Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,63 @@
1+
use bytes::Bytes;
12
use magnus::{
23
value::{InnerValue, Opaque, ReprValue},
34
RString, Ruby,
45
};
5-
use std::io;
6-
use std::io::ErrorKind;
6+
use std::io::Write;
7+
use std::sync::{Arc, Mutex};
8+
use wasmtime_wasi::{OutputStream, Pollable, StdoutStream, StreamError, StreamResult};
9+
10+
pub struct WritePipe {
11+
inner: Arc<Mutex<OutputLimitedBuffer>>,
12+
}
13+
14+
impl WritePipe {
15+
pub fn new(inner: OutputLimitedBuffer) -> Self {
16+
Self {
17+
inner: Arc::new(Mutex::new(inner)),
18+
}
19+
}
20+
}
21+
22+
impl Clone for WritePipe {
23+
fn clone(&self) -> Self {
24+
Self {
25+
inner: Arc::clone(&self.inner),
26+
}
27+
}
28+
}
29+
30+
impl StdoutStream for WritePipe {
31+
fn stream(&self) -> Box<dyn wasmtime_wasi::OutputStream> {
32+
let cloned = self.clone();
33+
Box::new(cloned)
34+
}
35+
36+
fn isatty(&self) -> bool {
37+
false
38+
}
39+
}
40+
41+
#[async_trait::async_trait]
42+
impl Pollable for WritePipe {
43+
async fn ready(&mut self) {}
44+
}
45+
46+
impl OutputStream for WritePipe {
47+
fn write(&mut self, bytes: Bytes) -> StreamResult<()> {
48+
let mut stream = self.inner.lock().expect("Should be only writer");
49+
stream.write(&bytes)
50+
}
51+
52+
fn flush(&mut self) -> StreamResult<()> {
53+
Ok(())
54+
}
55+
56+
fn check_write(&mut self) -> StreamResult<usize> {
57+
let mut stream = self.inner.lock().expect("Should be only writer");
58+
stream.check_write()
59+
}
60+
}
761

862
/// A buffer that limits the number of bytes that can be written to it.
963
/// If the buffer is full, it will truncate the data.
@@ -21,8 +75,12 @@ impl OutputLimitedBuffer {
2175
}
2276
}
2377

24-
impl io::Write for OutputLimitedBuffer {
25-
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
78+
impl OutputLimitedBuffer {
79+
fn check_write(&mut self) -> StreamResult<usize> {
80+
Ok(usize::MAX)
81+
}
82+
83+
fn write(&mut self, buf: &[u8]) -> StreamResult<()> {
2684
// Append a buffer to the string and truncate when hitting the capacity.
2785
// We return the input buffer size regardless of whether we truncated or not to avoid a panic.
2886
let ruby = Ruby::get().unwrap();
@@ -32,39 +90,34 @@ impl io::Write for OutputLimitedBuffer {
3290
// Handling frozen case here is necessary because magnus does not check if a string is frozen before writing to it.
3391
let is_frozen = inner_buffer.as_value().is_frozen();
3492
if is_frozen {
35-
return Err(io::Error::new(
36-
ErrorKind::WriteZero,
37-
"Cannot write to a frozen buffer.",
38-
));
93+
return Err(StreamError::trap("Cannot write to a frozen buffer."));
3994
}
4095

4196
if buf.is_empty() {
42-
return Ok(0);
97+
return Ok(());
4398
}
4499

45100
if inner_buffer
46101
.len()
47102
.checked_add(buf.len())
48103
.is_some_and(|val| val < self.capacity)
49104
{
50-
let amount_written = inner_buffer.write(buf)?;
105+
let amount_written = inner_buffer
106+
.write(buf)
107+
.map_err(|e| StreamError::trap(&e.to_string()))?;
51108
if amount_written < buf.len() {
52-
return Ok(amount_written);
109+
return Ok(());
53110
}
54111
} else {
55112
let portion = self.capacity - inner_buffer.len();
56-
let amount_written = inner_buffer.write(&buf[0..portion])?;
113+
let amount_written = inner_buffer
114+
.write(&buf[0..portion])
115+
.map_err(|e| StreamError::trap(&e.to_string()))?;
57116
if amount_written < portion {
58-
return Ok(amount_written);
117+
return Ok(());
59118
}
60119
};
61120

62-
Ok(buf.len())
63-
}
64-
65-
fn flush(&mut self) -> io::Result<()> {
66-
let ruby = Ruby::get().unwrap();
67-
68-
self.buffer.get_inner_with(&ruby).flush()
121+
Ok(())
69122
}
70123
}

ext/src/ruby_api/linker.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ impl Linker {
5454

5555
let mut inner: LinkerImpl<StoreData> = LinkerImpl::new(engine.get());
5656
if wasi {
57-
wasi_common::sync::add_to_linker(&mut inner, |s| s.wasi_ctx_mut())
58-
.map_err(|e| error!("{}", e))?
57+
wasmtime_wasi::preview0::add_to_linker_sync(&mut inner, |s| s.wasi_ctx_mut())
58+
.map_err(|e| error!("{}", e))?;
59+
wasmtime_wasi::preview1::add_to_linker_sync(&mut inner, |s| s.wasi_ctx_mut())
60+
.map_err(|e| error!("{}", e))?;
5961
}
6062
Ok(Self {
6163
inner: RefCell::new(inner),

ext/src/ruby_api/store.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ use rb_sys::tracking_allocator::{ManuallyTracked, TrackingAllocator};
1515
use std::borrow::Borrow;
1616
use std::cell::UnsafeCell;
1717
use std::convert::TryFrom;
18-
use wasi_common::{I32Exit, WasiCtx as WasiCtxImpl};
1918
use wasmtime::{
2019
AsContext, AsContextMut, ResourceLimiter, Store as StoreImpl, StoreContext, StoreContextMut,
2120
StoreLimits, StoreLimitsBuilder,
2221
};
22+
use wasmtime_wasi::preview1::WasiP1Ctx as WasiCtxImpl;
23+
use wasmtime_wasi::I32Exit;
2324

2425
define_rb_intern!(
2526
WASI_CTX => "wasi_ctx",

ext/src/ruby_api/wasi_ctx.rs

Lines changed: 2 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,16 @@ use super::{
55
};
66
use crate::error;
77
use crate::helpers::OutputLimitedBuffer;
8-
use deterministic_wasi_ctx::build_wasi_ctx as wasi_deterministic_ctx;
98
use magnus::{
109
class, function, gc::Marker, method, prelude::*, typed_data::Obj, Error, Object, RString,
1110
RTypedData, Ruby, TypedData, Value,
1211
};
1312
use std::{borrow::Borrow, cell::RefCell, fs::File, path::PathBuf};
14-
use wasi_common::pipe::{ReadPipe, WritePipe};
15-
use wasi_common::WasiCtx as WasiCtxImpl;
13+
use wasmtime_wasi::preview1::WasiP1Ctx as WasiCtxImpl;
1614

1715
/// @yard
1816
/// WASI context to be sent as {Store#new}’s +wasi_ctx+ keyword argument.
1917
///
20-
/// Instance methods mutate the current object and return +self+.
21-
///
2218
/// @see https://docs.rs/wasmtime-wasi/latest/wasmtime_wasi/struct.WasiCtx.html
2319
/// Wasmtime's Rust doc
2420
#[magnus::wrap(class = "Wasmtime::WasiCtx", size, free_immediately)]
@@ -29,96 +25,6 @@ pub struct WasiCtx {
2925
type RbSelf = Obj<WasiCtx>;
3026

3127
impl WasiCtx {
32-
/// @yard
33-
/// Create a new deterministic {WasiCtx}. See https://github.com/Shopify/deterministic-wasi-ctx for more details
34-
/// @return [WasiCtx]
35-
pub fn deterministic() -> Self {
36-
Self {
37-
inner: RefCell::new(wasi_deterministic_ctx()),
38-
}
39-
}
40-
41-
/// @yard
42-
/// Set stdin to read from the specified file.
43-
/// @def set_stdin_file(path)
44-
/// @param path [String] The path of the file to read from.
45-
/// @return [WasiCtxBuilder] +self+
46-
fn set_stdin_file(rb_self: RbSelf, path: RString) -> RbSelf {
47-
let inner = rb_self.inner.borrow_mut();
48-
let cs = file_r(path).map(wasi_file).unwrap();
49-
inner.set_stdin(cs);
50-
rb_self
51-
}
52-
53-
/// @yard
54-
/// Set stdin to the specified String.
55-
/// @def set_stdin_string(content)
56-
/// @param content [String]
57-
/// @return [WasiCtx] +self+
58-
fn set_stdin_string(rb_self: RbSelf, content: RString) -> RbSelf {
59-
let inner = rb_self.inner.borrow_mut();
60-
let str = unsafe { content.as_slice() };
61-
let pipe = ReadPipe::from(str);
62-
inner.set_stdin(Box::new(pipe));
63-
rb_self
64-
}
65-
66-
/// @yard
67-
/// Set stdout to write to a file. Will truncate the file if it exists,
68-
/// otherwise try to create it.
69-
/// @def set_stdout_file(path)
70-
/// @param path [String] The path of the file to write to.
71-
/// @return [WasiCtx] +self+
72-
fn set_stdout_file(rb_self: RbSelf, path: RString) -> RbSelf {
73-
let inner = rb_self.inner.borrow_mut();
74-
let cs = file_w(path).map(wasi_file).unwrap();
75-
inner.set_stdout(cs);
76-
rb_self
77-
}
78-
79-
/// @yard
80-
/// Set stdout to write to a string buffer.
81-
/// If the string buffer is frozen, Wasm execution will raise a Wasmtime::Error error.
82-
/// No encoding checks are done on the resulting string, it is the caller's responsibility to ensure the string contains a valid encoding
83-
/// @def set_stdout_buffer(buffer, capacity)
84-
/// @param buffer [String] The string buffer to write to.
85-
/// @param capacity [Integer] The maximum number of bytes that can be written to the output buffer.
86-
/// @return [WasiCtx] +self+
87-
fn set_stdout_buffer(rb_self: RbSelf, buffer: RString, capacity: usize) -> RbSelf {
88-
let inner = rb_self.inner.borrow_mut();
89-
let pipe = WritePipe::new(OutputLimitedBuffer::new(buffer.into(), capacity));
90-
inner.set_stdout(Box::new(pipe));
91-
rb_self
92-
}
93-
94-
/// @yard
95-
/// Set stderr to write to a file. Will truncate the file if it exists,
96-
/// otherwise try to create it.
97-
/// @def set_stderr_file(path)
98-
/// @param path [String] The path of the file to write to.
99-
/// @return [WasiCtx] +self+
100-
fn set_stderr_file(rb_self: RbSelf, path: RString) -> RbSelf {
101-
let inner = rb_self.inner.borrow_mut();
102-
let cs = file_w(path).map(wasi_file).unwrap();
103-
inner.set_stderr(cs);
104-
rb_self
105-
}
106-
107-
/// @yard
108-
/// Set stderr to write to a string buffer.
109-
/// If the string buffer is frozen, Wasm execution will raise a Wasmtime::Error error.
110-
/// No encoding checks are done on the resulting string, it is the caller's responsibility to ensure the string contains a valid encoding
111-
/// @def set_stderr_buffer(buffer, capacity)
112-
/// @param buffer [String] The string buffer to write to.
113-
/// @param capacity [Integer] The maximum number of bytes that can be written to the output buffer.
114-
/// @return [WasiCtx] +self+
115-
fn set_stderr_buffer(rb_self: RbSelf, buffer: RString, capacity: usize) -> RbSelf {
116-
let inner = rb_self.inner.borrow_mut();
117-
let pipe = WritePipe::new(OutputLimitedBuffer::new(buffer.into(), capacity));
118-
inner.set_stderr(Box::new(pipe));
119-
rb_self
120-
}
121-
12228
pub fn from_inner(inner: WasiCtxImpl) -> Self {
12329
Self {
12430
inner: RefCell::new(inner),
@@ -131,13 +37,6 @@ impl WasiCtx {
13137
}
13238

13339
pub fn init() -> Result<(), Error> {
134-
let class = root().define_class("WasiCtx", class::object())?;
135-
class.define_singleton_method("deterministic", function!(WasiCtx::deterministic, 0))?;
136-
class.define_method("set_stdin_file", method!(WasiCtx::set_stdin_file, 1))?;
137-
class.define_method("set_stdin_string", method!(WasiCtx::set_stdin_string, 1))?;
138-
class.define_method("set_stdout_file", method!(WasiCtx::set_stdout_file, 1))?;
139-
class.define_method("set_stdout_buffer", method!(WasiCtx::set_stdout_buffer, 2))?;
140-
class.define_method("set_stderr_file", method!(WasiCtx::set_stderr_file, 1))?;
141-
class.define_method("set_stderr_buffer", method!(WasiCtx::set_stderr_buffer, 2))?;
40+
root().define_class("WasiCtx", class::object())?;
14241
Ok(())
14342
}

0 commit comments

Comments
 (0)