Skip to content

Commit da81f96

Browse files
committed
feat(rpc): implement wasmtime_rpc::link_{item,instance}
Signed-off-by: Roman Volosatovs <[email protected]>
1 parent 9cea5e6 commit da81f96

File tree

7 files changed

+591
-52
lines changed

7 files changed

+591
-52
lines changed

crates/rpc/src/lib.rs

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ use futures::TryStreamExt as _;
1414
use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _};
1515
use tokio::try_join;
1616
use tokio_util::codec::Encoder;
17-
use tracing::{instrument, warn};
17+
use tracing::{debug, instrument, trace, warn};
1818
use wasm_tokio::cm::AsyncReadValue as _;
1919
use wasm_tokio::{
2020
AsyncReadCore as _, AsyncReadLeb128 as _, AsyncReadUtf8 as _, CoreStringEncoder, Leb128Encoder,
2121
Utf8Encoder,
2222
};
2323
use wasmtime::component::types::{self, Case, Field};
2424
use wasmtime::component::{LinkerInstance, Type, Val};
25-
use wasmtime::{AsContextMut, StoreContextMut};
25+
use wasmtime::{AsContextMut, Engine, StoreContextMut};
2626
use wasmtime_wasi::WasiView;
2727
use wrpc_transport::{Index as _, Invocation, Invoke, Session};
2828

@@ -44,6 +44,7 @@ impl<T, W> ValEncoder<'_, T, W> {
4444
}
4545
}
4646

47+
#[must_use]
4748
pub fn with_type<'a>(&'a mut self, ty: &'a Type) -> ValEncoder<'a, T, W> {
4849
ValEncoder {
4950
store: self.store.as_context_mut(),
@@ -446,7 +447,9 @@ async fn read_flags(n: usize, r: &mut (impl AsyncRead + Unpin)) -> std::io::Resu
446447
Ok(u128::from_le_bytes(buf))
447448
}
448449

449-
pub async fn read_value<T, R>(
450+
/// Read encoded value of type [`Type`] from an [`AsyncRead`] into a [`Val`]
451+
#[instrument(level = "trace", skip_all, fields(ty, path))]
452+
async fn read_value<T, R>(
450453
store: &mut impl AsContextMut<Data = T>,
451454
r: &mut Pin<&mut R>,
452455
val: &mut Val,
@@ -534,6 +537,7 @@ where
534537
for i in 0..n {
535538
let mut v = Val::Bool(false);
536539
path.push(i);
540+
trace!(i, "reading list element value");
537541
Box::pin(read_value(store, r, &mut v, &ty, &path)).await?;
538542
path.pop();
539543
vs.push(v);
@@ -548,6 +552,7 @@ where
548552
for (i, Field { name, ty }) in fields.enumerate() {
549553
let mut v = Val::Bool(false);
550554
path.push(i);
555+
trace!(i, "reading struct field value");
551556
Box::pin(read_value(store, r, &mut v, &ty, &path)).await?;
552557
path.pop();
553558
vs.push((name.to_string(), v));
@@ -562,6 +567,7 @@ where
562567
for (i, ty) in types.enumerate() {
563568
let mut v = Val::Bool(false);
564569
path.push(i);
570+
trace!(i, "reading tuple element value");
565571
Box::pin(read_value(store, r, &mut v, &ty, &path)).await?;
566572
path.pop();
567573
vs.push(v);
@@ -583,6 +589,7 @@ where
583589
let name = name.to_string();
584590
if let Some(ty) = ty {
585591
let mut v = Val::Bool(false);
592+
trace!(variant = name, "reading nested variant value");
586593
Box::pin(read_value(store, r, &mut v, &ty, path)).await?;
587594
*val = Val::Variant(name, Some(Box::new(v)));
588595
} else {
@@ -608,6 +615,7 @@ where
608615
let ok = r.read_option_status().await?;
609616
if ok {
610617
let mut v = Val::Bool(false);
618+
trace!("reading nested `option::some` value");
611619
Box::pin(read_value(store, r, &mut v, &ty.ty(), path)).await?;
612620
*val = Val::Option(Some(Box::new(v)));
613621
} else {
@@ -620,13 +628,15 @@ where
620628
if ok {
621629
if let Some(ty) = ty.ok() {
622630
let mut v = Val::Bool(false);
631+
trace!("reading nested `result::ok` value");
623632
Box::pin(read_value(store, r, &mut v, &ty, path)).await?;
624633
*val = Val::Result(Ok(Some(Box::new(v))));
625634
} else {
626635
*val = Val::Result(Ok(None));
627636
}
628637
} else if let Some(ty) = ty.err() {
629638
let mut v = Val::Bool(false);
639+
trace!("reading nested `result::err` value");
630640
Box::pin(read_value(store, r, &mut v, &ty, path)).await?;
631641
*val = Val::Result(Err(Some(Box::new(v))));
632642
} else {
@@ -696,6 +706,90 @@ pub trait WrpcView<C: Invoke>: Send {
696706
fn client(&self) -> &C;
697707
}
698708

709+
/// Polyfill [`types::ComponentItem`] in a [`LinkerInstance`] using [`wrpc_transport::Invoke`]
710+
#[instrument(level = "trace", skip_all)]
711+
pub fn link_item<'a, C, V>(
712+
engine: &Engine,
713+
linker: &mut LinkerInstance<V>,
714+
ty: types::ComponentItem,
715+
instance: impl Into<Arc<str>>,
716+
name: impl Into<Arc<str>>,
717+
cx: C::Context,
718+
) -> wasmtime::Result<()>
719+
where
720+
V: WrpcView<C> + WasiView,
721+
C: Invoke,
722+
C::Error: Into<wasmtime::Error>,
723+
C::Context: Clone + 'static,
724+
<C::Session as Session>::TransportError: Into<wasmtime::Error>,
725+
<C::Outgoing as wrpc_transport::Index<C::NestedOutgoing>>::Error: Into<wasmtime::Error>,
726+
C::NestedOutgoing: 'static,
727+
<C::NestedOutgoing as wrpc_transport::Index<C::NestedOutgoing>>::Error: Into<wasmtime::Error>,
728+
C::Incoming: Unpin + Sized + 'static,
729+
<C::Incoming as wrpc_transport::Index<C::Incoming>>::Error:
730+
Into<Box<dyn std::error::Error + Send + Sync>>,
731+
{
732+
let instance = instance.into();
733+
match ty {
734+
types::ComponentItem::ComponentFunc(ty) => {
735+
let name = name.into();
736+
debug!(?instance, ?name, "linking function");
737+
link_function(linker, ty, instance, name, cx)?
738+
}
739+
types::ComponentItem::CoreFunc(_) => {
740+
bail!("polyfilling core functions not supported yet")
741+
}
742+
types::ComponentItem::Module(_) => bail!("polyfilling modules not supported yet"),
743+
types::ComponentItem::Component(ty) => {
744+
for (name, ty) in ty.imports(&engine) {
745+
debug!(?instance, name, "linking component item");
746+
link_item(engine, linker, ty, "", name, cx.clone())?;
747+
}
748+
}
749+
types::ComponentItem::ComponentInstance(ty) => {
750+
let name = name.into();
751+
let mut linker = linker
752+
.instance(&name)
753+
.with_context(|| format!("failed to instantiate `{name}` in the linker"))?;
754+
debug!(?instance, ?name, "linking instance");
755+
link_instance(engine, &mut linker, ty, name, cx)?
756+
}
757+
types::ComponentItem::Type(_) => {}
758+
types::ComponentItem::Resource(_) => bail!("polyfilling resources not supported yet"),
759+
}
760+
Ok(())
761+
}
762+
763+
/// Polyfill [`types::ComponentInstance`] in a [`LinkerInstance`] using [`wrpc_transport::Invoke`]
764+
#[instrument(level = "trace", skip_all)]
765+
pub fn link_instance<'a, C, V>(
766+
engine: &Engine,
767+
linker: &mut LinkerInstance<V>,
768+
ty: types::ComponentInstance,
769+
name: impl Into<Arc<str>>,
770+
cx: C::Context,
771+
) -> wasmtime::Result<()>
772+
where
773+
V: WrpcView<C> + WasiView,
774+
C: Invoke,
775+
C::Error: Into<wasmtime::Error>,
776+
C::Context: Clone + 'static,
777+
<C::Session as Session>::TransportError: Into<wasmtime::Error>,
778+
<C::Outgoing as wrpc_transport::Index<C::NestedOutgoing>>::Error: Into<wasmtime::Error>,
779+
C::NestedOutgoing: 'static,
780+
<C::NestedOutgoing as wrpc_transport::Index<C::NestedOutgoing>>::Error: Into<wasmtime::Error>,
781+
C::Incoming: Unpin + Sized + 'static,
782+
<C::Incoming as wrpc_transport::Index<C::Incoming>>::Error:
783+
Into<Box<dyn std::error::Error + Send + Sync>>,
784+
{
785+
let instance = name.into();
786+
for (name, ty) in ty.exports(&engine) {
787+
debug!(name, "linking instance item");
788+
link_item(engine, linker, ty, Arc::clone(&instance), name, cx.clone())?
789+
}
790+
Ok(())
791+
}
792+
699793
/// Polyfill [`types::ComponentFunc`] in a [`LinkerInstance`] using [`wrpc_transport::Invoke`]
700794
#[instrument(level = "trace", skip_all)]
701795
pub fn link_function<'a, C, V>(
@@ -769,7 +863,7 @@ where
769863
for (i, (v, ref ty)) in zip(results, ty.results()).enumerate() {
770864
read_value(&mut store, &mut incoming, v, ty, &[i])
771865
.await
772-
.context("failed to decode result value")?;
866+
.with_context(|| format!("failed to decode return value {i}"))?;
773867
}
774868
Ok(())
775869
},

0 commit comments

Comments
 (0)