Skip to content

Commit a5c07fd

Browse files
committed
add rough native plugin sketch
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
1 parent 5d97bc3 commit a5c07fd

File tree

12 files changed

+405
-0
lines changed

12 files changed

+405
-0
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ members = [
174174
"examples/min-platform",
175175
"examples/min-platform/embedding",
176176
"examples/wasip2-plugins",
177+
"examples/native-plugin",
178+
"examples/native-plugin/rust-plugin",
179+
"examples/native-plugin/wasm",
177180
"fuzz",
178181
"winch/codegen",
179182
]

crates/wasmtime/src/runtime/component/func/host.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,25 @@ impl HostFunc {
118118
}
119119
}
120120

121+
pub(crate) unsafe fn new_unchecked<T, F>(func: F) -> Arc<HostFunc>
122+
where
123+
T: 'static,
124+
F: Fn(StoreContextMut<'_, T>, ComponentFunc, &mut [MaybeUninit<ValRaw>]) -> Result<()>
125+
+ Send
126+
+ Sync
127+
+ 'static,
128+
{
129+
let entrypoint = raw_entrypoint::<T, F>;
130+
Arc::new(HostFunc {
131+
entrypoint,
132+
// This function performs external type checks and subsequently does
133+
// not need to perform up-front type checks. Instead everything is
134+
// dynamically managed at runtime.
135+
typecheck: Box::new(move |_expected_index, _expected_types| Ok(())),
136+
func: Box::new(func),
137+
})
138+
}
139+
121140
fn new_dynamic_canonical<T, F>(func: F) -> Arc<HostFunc>
122141
where
123142
F: Fn(
@@ -664,6 +683,55 @@ where
664683
}
665684
}
666685

686+
unsafe fn call_raw<T, F>(
687+
mut store: StoreContextMut<'_, T>,
688+
instance: Instance,
689+
ty: TypeFuncIndex,
690+
options_idx: OptionsIndex,
691+
storage: &mut [MaybeUninit<ValRaw>],
692+
closure: F,
693+
) -> Result<()>
694+
where
695+
F: Fn(StoreContextMut<'_, T>, ComponentFunc, &mut [MaybeUninit<ValRaw>]) -> Result<()>
696+
+ Send
697+
+ Sync
698+
+ 'static,
699+
{
700+
let options = Options::new_index(store.0, instance, options_idx);
701+
let vminstance = instance.id().get(store.0);
702+
let opts = &vminstance.component().env_component().options[options_idx];
703+
let caller_instance = opts.instance;
704+
let mut flags = vminstance.instance_flags(caller_instance);
705+
706+
// Perform a dynamic check that this instance can indeed be left. Exiting
707+
// the component is disallowed, for example, when the `realloc` function
708+
// calls a canonical import.
709+
if unsafe { !flags.may_leave() } {
710+
return Err(anyhow!(crate::Trap::CannotLeaveComponent));
711+
}
712+
713+
let types = instance.id().get(store.0).component().types().clone();
714+
715+
let lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance);
716+
lift.enter_call();
717+
let ty = ComponentFunc::from(ty, &lift.instance_type());
718+
719+
// TODO: Handle resources and async
720+
721+
closure(store.as_context_mut(), ty, storage)?;
722+
723+
unsafe {
724+
flags.set_may_leave(false);
725+
}
726+
let mut lower = LowerContext::new(store, &options, &types, instance);
727+
unsafe {
728+
flags.set_may_leave(true);
729+
}
730+
lower.exit_call()?;
731+
732+
return Ok(());
733+
}
734+
667735
pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
668736
// FIXME(#4311): needs memory64 support
669737
let ptr = usize::try_from(ptr.get_u32())?;
@@ -916,6 +984,36 @@ pub(crate) fn validate_inbounds_dynamic(
916984
Ok(ptr)
917985
}
918986

987+
extern "C" fn raw_entrypoint<T, F>(
988+
cx: NonNull<VMOpaqueContext>,
989+
data: NonNull<u8>,
990+
ty: u32,
991+
options: u32,
992+
storage: NonNull<MaybeUninit<ValRaw>>,
993+
storage_len: usize,
994+
) -> bool
995+
where
996+
F: Fn(StoreContextMut<'_, T>, ComponentFunc, &mut [MaybeUninit<ValRaw>]) -> Result<()>
997+
+ Send
998+
+ Sync
999+
+ 'static,
1000+
T: 'static,
1001+
{
1002+
let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
1003+
unsafe {
1004+
call_host_and_handle_result(cx, |store, instance| {
1005+
call_raw::<T, _>(
1006+
store,
1007+
instance,
1008+
TypeFuncIndex::from_u32(ty),
1009+
OptionsIndex::from_u32(options),
1010+
NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
1011+
&*data.as_ptr(),
1012+
)
1013+
})
1014+
}
1015+
}
1016+
9191017
extern "C" fn dynamic_entrypoint<T, F>(
9201018
cx: NonNull<VMOpaqueContext>,
9211019
data: NonNull<u8>,

crates/wasmtime/src/runtime/component/linker.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::ValRaw;
12
#[cfg(feature = "component-model-async")]
23
use crate::component::concurrent::Accessor;
34
use crate::component::func::HostFunc;
@@ -12,6 +13,7 @@ use crate::prelude::*;
1213
use crate::{AsContextMut, Engine, Module, StoreContextMut};
1314
use alloc::sync::Arc;
1415
use core::marker;
16+
use core::mem::MaybeUninit;
1517
#[cfg(feature = "async")]
1618
use core::{future::Future, pin::Pin};
1719
use wasmtime_environ::PrimaryMap;
@@ -674,6 +676,37 @@ impl<T: 'static> LinkerInstance<'_, T> {
674676
Ok(())
675677
}
676678

679+
/// Creates a [`Func::new_unchecked`]-style function named in this linker.
680+
///
681+
/// For more information see [`Linker::func_wrap`].
682+
///
683+
/// # Panics
684+
///
685+
/// Panics if the given function type is not associated with the same engine
686+
/// as this linker.
687+
///
688+
/// # Safety
689+
///
690+
/// See [`Func::new_unchecked`] for more safety information.
691+
pub unsafe fn func_new_unchecked(
692+
&mut self,
693+
name: &str,
694+
func: impl Fn(
695+
StoreContextMut<'_, T>,
696+
types::ComponentFunc,
697+
&mut [MaybeUninit<ValRaw>],
698+
) -> Result<()>
699+
+ Send
700+
+ Sync
701+
+ 'static,
702+
) -> Result<()> {
703+
self.insert(
704+
name,
705+
Definition::Func(unsafe { HostFunc::new_unchecked(func) }),
706+
)?;
707+
Ok(())
708+
}
709+
677710
/// Define a new host-provided async function using dynamic types.
678711
///
679712
/// As [`Self::func_wrap_async`] is a dual of [`Self::func_wrap`], this

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ create_rust_wasm(wasi wasm32-wasip1)
5959
create_rust_wasm(wasi wasm32-wasip2)
6060
create_rust_wasm(component wasm32-unknown-unknown)
6161
create_rust_wasm(resource-component wasm32-wasip2)
62+
create_rust_wasm(native-plugin wasm32-wasip2)
6263

6364
# C/C++ examples/tests
6465
create_target(anyref anyref.c)

examples/native-plugin/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "native-plugin"
3+
version.workspace = true
4+
authors.workspace = true
5+
edition.workspace = true
6+
rust-version.workspace = true
7+
publish = false
8+
9+
[dependencies]
10+
anyhow = { workspace = true }
11+
clap = { workspace = true, features = ["derive"] }
12+
libloading = "0.8"
13+
wasmtime = { workspace = true, features = ["std", "cranelift"] }
14+
wasmtime-wasi = { workspace = true }
15+
16+
[lints]
17+
workspace = true

examples/native-plugin/greet.wit

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package docs:greet;
2+
3+
interface greet {
4+
greet: func(name: string) -> string;
5+
}
6+
7+
world imports {
8+
import greet;
9+
}
10+
11+
world handler {
12+
export greet;
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "rust-plugin"
3+
version.workspace = true
4+
authors.workspace = true
5+
edition.workspace = true
6+
rust-version.workspace = true
7+
publish = false
8+
9+
[dependencies]
10+
wit-bindgen = { workspace = true, features = ["default"] }
11+
12+
[lib]
13+
path = "plugin.rs"
14+
name = "rust_plugin"
15+
crate-type = ["cdylib"]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
mod bindings {
2+
use crate::Handler;
3+
4+
wit_bindgen::generate!({
5+
path: "..",
6+
world: "handler",
7+
generate_all,
8+
});
9+
export!(Handler);
10+
}
11+
12+
use bindings::exports::docs::greet::greet::Guest;
13+
14+
struct Handler;
15+
16+
impl Guest for Handler {
17+
fn greet(name: String) -> String {
18+
format!("hello, {name}!")
19+
}
20+
}

0 commit comments

Comments
 (0)