Skip to content

Commit 8d5b0f6

Browse files
authored
Update to wasmtime 22 (#824)
This updates us to the latest version of wasmtime. The WASI API changed a lot, because WASI 0.2 got released since then. We still only support WASI 0.1 until there's practical toolchains for targeting WASI 0.2, so we can experiment with it. Unfortunately the new API does not allow us to define custom file system logic anymore, so mapping to Windows device paths is not possible anymore.
1 parent bb8cb41 commit 8d5b0f6

File tree

7 files changed

+35
-188
lines changed

7 files changed

+35
-188
lines changed

crates/livesplit-auto-splitting/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ sysinfo = { version = "0.30.0", default-features = false, features = [
2424
"multithread",
2525
] }
2626
time = { version = "0.3.3", default-features = false }
27-
wasmtime = { version = "17.0.0", default-features = false, features = [
27+
wasmtime = { version = "22.0.0", default-features = false, features = [
2828
"cranelift",
2929
"parallel-compilation",
30+
"runtime",
3031
] }
31-
wasmtime-wasi = { version = "17.0.0", default-features = false, features = [
32-
"sync",
32+
wasmtime-wasi = { version = "22.0.0", default-features = false, features = [
33+
"preview1",
3334
] }
34-
wasi-common = "17.0.0"
3535

3636
[target.'cfg(windows)'.dependencies]
3737
windows-sys = { version = "0.52.0", features = ["Win32_Storage_FileSystem"] }

crates/livesplit-auto-splitting/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ extern "C" {
154154
list_ptr: *mut ProcessId,
155155
list_len_ptr: *mut usize,
156156
) -> bool;
157-
/// Checks whether is a process is still open. You should detach from a
157+
/// Checks whether a process is still open. You should detach from a
158158
/// process and stop using it if this returns `false`.
159159
pub fn process_is_open(process: Process) -> bool;
160160
/// Reads memory from a process at the address given. This will write
@@ -534,8 +534,7 @@ support:
534534
nothing.
535535
- The file system is currently almost entirely empty. The host's file system is
536536
accessible through `/mnt`. It is entirely read-only. Windows paths are mapped
537-
to `/mnt/c`, `/mnt/d`, etc. to match WSL. Additionally `/mnt/device` maps to
538-
`\\?\` on Windows to access additional paths.
537+
to `/mnt/c`, `/mnt/d`, etc. to match WSL.
539538
- There are no environment variables.
540539
- There are no command line arguments.
541540
- There is no networking.

crates/livesplit-auto-splitting/src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
//! list_ptr: *mut ProcessId,
155155
//! list_len_ptr: *mut usize,
156156
//! ) -> bool;
157-
//! /// Checks whether is a process is still open. You should detach from a
157+
//! /// Checks whether a process is still open. You should detach from a
158158
//! /// process and stop using it if this returns `false`.
159159
//! pub fn process_is_open(process: Process) -> bool;
160160
//! /// Reads memory from a process at the address given. This will write
@@ -534,8 +534,7 @@
534534
//! nothing.
535535
//! - The file system is currently almost entirely empty. The host's file system
536536
//! is accessible through `/mnt`. It is entirely read-only. Windows paths are
537-
//! mapped to `/mnt/c`, `/mnt/d`, etc. to match WSL. Additionally
538-
//! `/mnt/device` maps to `\\?\` on Windows to access additional paths.
537+
//! mapped to `/mnt/c`, `/mnt/d`, etc. to match WSL.
539538
//! - There are no environment variables.
540539
//! - There are no command line arguments.
541540
//! - There is no networking.
Lines changed: 19 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
1-
use std::{
2-
path::{Path, PathBuf},
3-
str,
4-
};
1+
use std::{path::Path, str};
52

6-
use wasi_common::{
7-
dir::{OpenResult, ReaddirCursor, ReaddirEntity},
8-
file::{FdFlags, Filestat, OFlags},
9-
ErrorExt, WasiCtx, WasiDir,
10-
};
11-
use wasmtime_wasi::{ambient_authority, WasiCtxBuilder};
3+
use wasmtime_wasi::{preview1::WasiP1Ctx, DirPerms, FilePerms, WasiCtxBuilder};
124

135
use crate::wasi_path;
146

15-
pub fn build(script_path: Option<&Path>) -> WasiCtx {
16-
let mut wasi = WasiCtxBuilder::new().build();
7+
pub fn build(script_path: Option<&Path>) -> WasiP1Ctx {
8+
let mut wasi = WasiCtxBuilder::new();
179

1810
if let Some(script_path) = script_path {
1911
if let Some(path) = wasi_path::from_native(script_path) {
20-
let _ = wasi.push_env("SCRIPT_PATH", &path);
12+
wasi.env("SCRIPT_PATH", &path);
2113
}
2214
}
2315

@@ -32,164 +24,25 @@ pub fn build(script_path: Option<&Path>) -> WasiCtx {
3224
}
3325
drives &= !(1 << drive_idx);
3426
let drive = drive_idx as u8 + b'a';
35-
if let Ok(path) = wasmtime_wasi::Dir::open_ambient_dir(
27+
// Unfortunate if this fails, but we should still continue.
28+
let _ = wasi.preopened_dir(
3629
str::from_utf8(&[b'\\', b'\\', b'?', b'\\', drive, b':', b'\\']).unwrap(),
37-
ambient_authority(),
38-
) {
39-
wasi.push_dir(
40-
Box::new(ReadOnlyDir(wasmtime_wasi::dir::Dir::from_cap_std(path))),
41-
PathBuf::from(str::from_utf8(&[b'/', b'm', b'n', b't', b'/', drive]).unwrap()),
42-
)
43-
.unwrap();
44-
}
30+
str::from_utf8(&[b'/', b'm', b'n', b't', b'/', drive]).unwrap(),
31+
DirPerms::READ,
32+
FilePerms::READ,
33+
);
4534
}
4635

47-
wasi.push_dir(Box::new(DeviceDir), PathBuf::from("/mnt/device"))
48-
.unwrap();
36+
// FIXME: Unfortunately wasmtime doesn't support us defining our own
37+
// file system logic anymore.
38+
39+
// wasi.push_dir(Box::new(DeviceDir), PathBuf::from("/mnt/device"))
40+
// .unwrap();
4941
}
5042
#[cfg(not(windows))]
5143
{
52-
if let Ok(path) = wasmtime_wasi::Dir::open_ambient_dir("/", ambient_authority()) {
53-
wasi.push_dir(
54-
Box::new(ReadOnlyDir(wasmtime_wasi::dir::Dir::from_cap_std(path))),
55-
PathBuf::from("/mnt"),
56-
)
57-
.unwrap();
58-
}
59-
}
60-
wasi
61-
}
62-
63-
struct ReadOnlyDir(wasmtime_wasi::dir::Dir);
64-
65-
#[async_trait::async_trait]
66-
impl WasiDir for ReadOnlyDir {
67-
fn as_any(&self) -> &dyn std::any::Any {
68-
self
69-
}
70-
71-
async fn open_file(
72-
&self,
73-
symlink_follow: bool,
74-
path: &str,
75-
oflags: OFlags,
76-
read: bool,
77-
write: bool,
78-
fdflags: FdFlags,
79-
) -> Result<OpenResult, wasi_common::Error> {
80-
// We whitelist the OFlags and FdFlags to not accidentally allow
81-
// ways to modify the file system.
82-
const WHITELISTED_O_FLAGS: OFlags = OFlags::DIRECTORY;
83-
const WHITELISTED_FD_FLAGS: FdFlags = FdFlags::NONBLOCK;
84-
85-
if write || !WHITELISTED_O_FLAGS.contains(oflags) || !WHITELISTED_FD_FLAGS.contains(fdflags)
86-
{
87-
return Err(wasi_common::Error::not_supported());
88-
}
89-
90-
Ok(
91-
match self
92-
.0
93-
.open_file_(symlink_follow, path, oflags, read, write, fdflags)?
94-
{
95-
wasmtime_wasi::dir::OpenResult::Dir(d) => OpenResult::Dir(Box::new(ReadOnlyDir(d))),
96-
// We assume that wrapping the file type itself is not
97-
// necessary, because we ensure that the open flags don't allow
98-
// for any modifications anyway.
99-
wasmtime_wasi::dir::OpenResult::File(f) => OpenResult::File(Box::new(f)),
100-
},
101-
)
102-
}
103-
104-
async fn readdir(
105-
&self,
106-
cursor: ReaddirCursor,
107-
) -> Result<
108-
Box<dyn Iterator<Item = Result<ReaddirEntity, wasi_common::Error>> + Send>,
109-
wasi_common::Error,
110-
> {
111-
self.0.readdir(cursor).await
112-
}
113-
114-
async fn read_link(&self, path: &str) -> Result<PathBuf, wasi_common::Error> {
115-
self.0.read_link(path).await
116-
}
117-
118-
async fn get_filestat(&self) -> Result<Filestat, wasi_common::Error> {
119-
// FIXME: Make sure this says it's readonly, if it ever contains the
120-
// permissions.
121-
self.0.get_filestat().await
44+
// Unfortunate if this fails, but we should still continue.
45+
let _ = wasi.preopened_dir("/", "/mnt", DirPerms::READ, FilePerms::READ);
12246
}
123-
124-
async fn get_path_filestat(
125-
&self,
126-
path: &str,
127-
follow_symlinks: bool,
128-
) -> Result<Filestat, wasi_common::Error> {
129-
// FIXME: Make sure this says it's readonly, if it ever contains the
130-
// permissions.
131-
self.0.get_path_filestat(path, follow_symlinks).await
132-
}
133-
}
134-
135-
#[cfg(windows)]
136-
struct DeviceDir;
137-
138-
#[cfg(windows)]
139-
#[async_trait::async_trait]
140-
impl WasiDir for DeviceDir {
141-
fn as_any(&self) -> &dyn std::any::Any {
142-
self
143-
}
144-
145-
async fn open_file(
146-
&self,
147-
symlink_follow: bool,
148-
path: &str,
149-
oflags: OFlags,
150-
read: bool,
151-
write: bool,
152-
fdflags: FdFlags,
153-
) -> Result<OpenResult, wasi_common::Error> {
154-
let (dir, file) = device_path(path)?;
155-
dir.open_file(symlink_follow, file, oflags, read, write, fdflags)
156-
.await
157-
}
158-
159-
// FIXME: cap-primitives/src/windows/fs/get_path tries to strip `\\?\`,
160-
// which breaks paths that aren't valid without it, such as UNC paths:
161-
// https://github.com/bytecodealliance/cap-std/issues/348
162-
163-
async fn read_link(&self, path: &str) -> Result<PathBuf, wasi_common::Error> {
164-
let (dir, file) = device_path(path)?;
165-
dir.read_link(file).await
166-
}
167-
168-
async fn get_path_filestat(
169-
&self,
170-
path: &str,
171-
follow_symlinks: bool,
172-
) -> Result<Filestat, wasi_common::Error> {
173-
let (dir, file) = device_path(path)?;
174-
dir.get_path_filestat(file, follow_symlinks).await
175-
}
176-
}
177-
178-
#[cfg(windows)]
179-
fn device_path(path: &str) -> Result<(ReadOnlyDir, &str), wasi_common::Error> {
180-
let (parent, file) = path
181-
.strip_suffix('/')
182-
.unwrap_or(path)
183-
.rsplit_once('/')
184-
.ok_or_else(wasi_common::Error::not_supported)?;
185-
186-
let parent = wasi_path::to_native(&format!("/mnt/device/{parent}"), true)
187-
.ok_or_else(wasi_common::Error::not_supported)?;
188-
189-
let dir = wasmtime_wasi::dir::Dir::from_cap_std(
190-
wasmtime_wasi::Dir::open_ambient_dir(parent, ambient_authority())
191-
.map_err(|_| wasi_common::Error::not_supported())?,
192-
);
193-
194-
Ok((ReadOnlyDir(dir), file))
47+
wasi.build_p1()
19548
}

crates/livesplit-auto-splitting/src/runtime/mod.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use sysinfo::{ProcessRefreshKind, RefreshKind, System, UpdateKind};
1919
use wasmtime::{
2020
Engine, Extern, Linker, Memory, Module, OptLevel, Store, TypedFunc, WasmBacktraceDetails,
2121
};
22-
use wasmtime_wasi::WasiCtx;
22+
use wasmtime_wasi::preview1::WasiP1Ctx;
2323

2424
mod api;
2525

@@ -89,7 +89,7 @@ pub struct Context<T> {
8989
timer: T,
9090
memory: Option<Memory>,
9191
process_list: ProcessList,
92-
wasi: WasiCtx,
92+
wasi: WasiP1Ctx,
9393
}
9494

9595
/// A thread-safe handle used to interrupt the execution of the script.
@@ -396,11 +396,8 @@ impl CompiledAutoSplitter {
396396
.any(|import| import.module() == "wasi_snapshot_preview1");
397397

398398
if uses_wasi {
399-
wasmtime_wasi::snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(
400-
&mut linker,
401-
|ctx| &mut ctx.wasi,
402-
)
403-
.map_err(|source| CreationError::Wasi { source })?;
399+
wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |ctx| &mut ctx.wasi)
400+
.map_err(|source| CreationError::Wasi { source })?;
404401
}
405402

406403
let instance = linker

crates/livesplit-auto-splitting/src/timer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub enum TimerState {
1717
}
1818

1919
/// A timer that can be controlled by an auto splitter.
20-
pub trait Timer {
20+
pub trait Timer: Send {
2121
/// Returns the current state of the timer.
2222
fn state(&self) -> TimerState;
2323
/// Starts the timer.

src/auto_splitting/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,7 @@
534534
//! nothing.
535535
//! - The file system is currently almost entirely empty. The host's file system
536536
//! is accessible through `/mnt`. It is entirely read-only. Windows paths are
537-
//! mapped to `/mnt/c`, `/mnt/d`, etc. to match WSL. Additionally
538-
//! `/mnt/device` maps to `\\?\` on Windows to access additional paths.
537+
//! mapped to `/mnt/c`, `/mnt/d`, etc. to match WSL.
539538
//! - There are no environment variables.
540539
//! - There are no command line arguments.
541540
//! - There is no networking.
@@ -719,7 +718,7 @@ impl<T: event::CommandSink + TimerQuery + Send + 'static> Runtime<T> {
719718
// is an Arc<RwLock<T>>, so we can't implement the trait directly on it.
720719
struct Timer<E>(E);
721720

722-
impl<E: event::CommandSink + TimerQuery> AutoSplitTimer for Timer<E> {
721+
impl<E: event::CommandSink + TimerQuery + Send> AutoSplitTimer for Timer<E> {
723722
fn state(&self) -> TimerState {
724723
match self.0.get_timer().current_phase() {
725724
TimerPhase::NotRunning => TimerState::NotRunning,
@@ -770,7 +769,7 @@ impl<E: event::CommandSink + TimerQuery> AutoSplitTimer for Timer<E> {
770769
}
771770
}
772771

773-
async fn run<T: event::CommandSink + TimerQuery>(
772+
async fn run<T: event::CommandSink + TimerQuery + Send>(
774773
mut auto_splitter: watch::Receiver<Option<AutoSplitter<Timer<T>>>>,
775774
timeout_sender: watch::Sender<Option<Instant>>,
776775
interrupt_sender: watch::Sender<Option<InterruptHandle>>,

0 commit comments

Comments
 (0)