From 085ff96aa1237583f47845d50e06bb301ad88504 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 22 Feb 2024 12:07:35 +0100 Subject: [PATCH 1/5] Support resources in wasmtime encode/decode --- wasm-rpc/src/wasmtime.rs | 98 ++++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/wasm-rpc/src/wasmtime.rs b/wasm-rpc/src/wasmtime.rs index 1d127a28..15d48b89 100644 --- a/wasm-rpc/src/wasmtime.rs +++ b/wasm-rpc/src/wasmtime.rs @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::Value; +use crate::{Uri, Value}; use wasmtime::component::{ - types, Enum, Flags, List, OptionVal, Record, ResultVal, Tuple, Type, Val, Variant, + types, Enum, Flags, List, OptionVal, Record, ResourceAny, ResultVal, Tuple, Type, Val, Variant, }; pub enum EncodingError { @@ -23,8 +23,19 @@ pub enum EncodingError { Unknown { details: String }, } +pub trait ResourceStore { + fn self_uri(&self) -> Uri; + fn add(&self, resource: ResourceAny) -> u64; + fn borrow(&self, resource_id: u64) -> Option; + fn remove(&self, resource_id: u64) -> Option; +} + /// Converts a Value to a wasmtime Val based on the available type information. -pub fn decode_param(param: &Value, param_type: &Type) -> Result { +pub fn decode_param( + param: &Value, + param_type: &Type, + resource_store: &impl ResourceStore, +) -> Result { match param_type { Type::Bool => match param { Value::Bool(bool) => Ok(Val::Bool(*bool)), @@ -82,7 +93,7 @@ pub fn decode_param(param: &Value, param_type: &Type) -> Result { let decoded_values = values .iter() - .map(|v| decode_param(v, &ty.ty())) + .map(|v| decode_param(v, &ty.ty(), resource_store)) .collect::, EncodingError>>()?; let list = List::new(ty, decoded_values.into_boxed_slice()) .expect("Type mismatch in decode_param"); @@ -96,7 +107,7 @@ pub fn decode_param(param: &Value, param_type: &Type) -> Result, EncodingError>>()?; @@ -110,7 +121,7 @@ pub fn decode_param(param: &Value, param_type: &Type) -> Result = values .iter() .zip(ty.types()) - .map(|(value, ty)| decode_param(value, &ty)) + .map(|(value, ty)| decode_param(value, &ty, resource_store)) .collect::, EncodingError>>()?; let tuple = Tuple::new(ty, tuple_values.into_boxed_slice()) .expect("Type mismatch in decode_param"); @@ -138,7 +149,7 @@ pub fn decode_param(param: &Value, param_type: &Type) -> Result Result match param { Value::Option(value) => match value { Some(value) => { - let decoded_value = decode_param(value, &ty.ty())?; + let decoded_value = decode_param(value, &ty.ty(), resource_store)?; let option = OptionVal::new(ty, Some(decoded_value)) .expect("Type mismatch in decode_param"); Ok(Val::Option(option)) @@ -186,7 +197,7 @@ pub fn decode_param(param: &Value, param_type: &Type) -> Result Result Result Err(EncodingError::ParamTypeMismatch), }, - Type::Own(_) => Err(EncodingError::ParamTypeMismatch), - Type::Borrow(_) => Err(EncodingError::ParamTypeMismatch), + Type::Own(_) => match param { + Value::Handle { uri, resource_id } => { + if resource_store.self_uri() == *uri { + match resource_store.remove(*resource_id) { + Some(resource) => Ok(Val::Resource(resource)), + None => Err(EncodingError::ValueMismatch { + details: "resource not found".to_string(), + }), + } + } else { + Err(EncodingError::ValueMismatch { + details: "cannot resolve handle belonging to a different worker" + .to_string(), + }) + } + } + _ => Err(EncodingError::ParamTypeMismatch), + }, + Type::Borrow(_) => match param { + Value::Handle { uri, resource_id } => { + if resource_store.self_uri() == *uri { + match resource_store.borrow(*resource_id) { + Some(resource) => Ok(Val::Resource(resource)), + None => Err(EncodingError::ValueMismatch { + details: "resource not found".to_string(), + }), + } + } else { + Err(EncodingError::ValueMismatch { + details: "cannot resolve handle belonging to a different worker" + .to_string(), + }) + } + } + _ => Err(EncodingError::ParamTypeMismatch), + }, } } /// Converts a wasmtime Val to a Golem protobuf Val -pub fn encode_output(value: &Val) -> Result { +pub fn encode_output( + value: &Val, + resource_store: &impl ResourceStore, +) -> Result { match value { Val::Bool(bool) => Ok(Value::Bool(*bool)), Val::S8(i8) => Ok(Value::S8(*i8)), @@ -245,14 +293,14 @@ pub fn encode_output(value: &Val) -> Result { Val::List(list) => { let mut encoded_values = Vec::new(); for value in (*list).iter() { - encoded_values.push(encode_output(value)?); + encoded_values.push(encode_output(value, resource_store)?); } Ok(Value::List(encoded_values)) } Val::Record(record) => { let encoded_values = record .fields() - .map(|(_, value)| encode_output(value)) + .map(|(_, value)| encode_output(value, resource_store)) .collect::, EncodingError>>()?; Ok(Value::Record(encoded_values)) } @@ -260,7 +308,7 @@ pub fn encode_output(value: &Val) -> Result { let encoded_values = tuple .values() .iter() - .map(encode_output) + .map(|v| encode_output(v, resource_store)) .collect::, EncodingError>>()?; Ok(Value::Tuple(encoded_values)) } @@ -271,7 +319,7 @@ pub fn encode_output(value: &Val) -> Result { discriminant, value, } = wasm_variant; - let encoded_output = value.map(|v| encode_output(&v)).transpose()?; + let encoded_output = value.map(|v| encode_output(&v, resource_store)).transpose()?; Ok(Value::Variant { case_idx: discriminant, case_value: encoded_output.map(Box::new), @@ -287,18 +335,18 @@ pub fn encode_output(value: &Val) -> Result { } Val::Option(option) => match option.value() { Some(value) => { - let encoded_output = encode_output(value)?; + let encoded_output = encode_output(value, resource_store)?; Ok(Value::Option(Some(Box::new(encoded_output)))) } None => Ok(Value::Option(None)), }, Val::Result(result) => match result.value() { Ok(value) => { - let encoded_output = value.map(encode_output).transpose()?; + let encoded_output = value.map(|v| encode_output(v, resource_store)).transpose()?; Ok(Value::Result(Ok(encoded_output.map(Box::new)))) } Err(value) => { - let encoded_output = value.map(encode_output).transpose()?; + let encoded_output = value.map(|v| encode_output(v, resource_store)).transpose()?; Ok(Value::Result(Err(encoded_output.map(Box::new)))) } }, @@ -321,9 +369,13 @@ pub fn encode_output(value: &Val) -> Result { } Ok(Value::Flags(encoded_value)) } - Val::Resource(_) => Err(EncodingError::Unknown { - details: "resource values are not supported yet".to_string(), - }), + Val::Resource(resource) => { + let id = resource_store.add(resource.clone()); + Ok(Value::Handle { + uri: resource_store.self_uri(), + resource_id: id, + }) + } } } From 32701b833300b9e2771d69331aa426f47cd7f311 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 22 Feb 2024 13:38:25 +0100 Subject: [PATCH 2/5] mut --- wasm-rpc/src/wasmtime.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/wasm-rpc/src/wasmtime.rs b/wasm-rpc/src/wasmtime.rs index 15d48b89..02ae92d2 100644 --- a/wasm-rpc/src/wasmtime.rs +++ b/wasm-rpc/src/wasmtime.rs @@ -25,16 +25,16 @@ pub enum EncodingError { pub trait ResourceStore { fn self_uri(&self) -> Uri; - fn add(&self, resource: ResourceAny) -> u64; + fn add(&mut self, resource: ResourceAny) -> u64; fn borrow(&self, resource_id: u64) -> Option; - fn remove(&self, resource_id: u64) -> Option; + fn remove(&mut self, resource_id: u64) -> Option; } /// Converts a Value to a wasmtime Val based on the available type information. pub fn decode_param( param: &Value, param_type: &Type, - resource_store: &impl ResourceStore, + resource_store: &mut impl ResourceStore, ) -> Result { match param_type { Type::Bool => match param { @@ -274,7 +274,7 @@ pub fn decode_param( /// Converts a wasmtime Val to a Golem protobuf Val pub fn encode_output( value: &Val, - resource_store: &impl ResourceStore, + resource_store: &mut impl ResourceStore, ) -> Result { match value { Val::Bool(bool) => Ok(Value::Bool(*bool)), @@ -319,7 +319,9 @@ pub fn encode_output( discriminant, value, } = wasm_variant; - let encoded_output = value.map(|v| encode_output(&v, resource_store)).transpose()?; + let encoded_output = value + .map(|v| encode_output(&v, resource_store)) + .transpose()?; Ok(Value::Variant { case_idx: discriminant, case_value: encoded_output.map(Box::new), @@ -342,11 +344,15 @@ pub fn encode_output( }, Val::Result(result) => match result.value() { Ok(value) => { - let encoded_output = value.map(|v| encode_output(v, resource_store)).transpose()?; + let encoded_output = value + .map(|v| encode_output(v, resource_store)) + .transpose()?; Ok(Value::Result(Ok(encoded_output.map(Box::new)))) } Err(value) => { - let encoded_output = value.map(|v| encode_output(v, resource_store)).transpose()?; + let encoded_output = value + .map(|v| encode_output(v, resource_store)) + .transpose()?; Ok(Value::Result(Err(encoded_output.map(Box::new)))) } }, From 0a2d05d0435baa9f7bca0003fdd6e705325f107b Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 22 Feb 2024 17:18:33 +0100 Subject: [PATCH 3/5] Fix names --- wasm-rpc-stubgen/src/stub.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/wasm-rpc-stubgen/src/stub.rs b/wasm-rpc-stubgen/src/stub.rs index 7465fd36..aed89acb 100644 --- a/wasm-rpc-stubgen/src/stub.rs +++ b/wasm-rpc-stubgen/src/stub.rs @@ -292,6 +292,9 @@ fn collect_stub_interfaces(resolve: &Resolve, world: &World) -> anyhow::Result anyhow::Result( } fn collect_stub_resources<'a>( + owner_interface: &str, types: impl Iterator, resolve: &'a Resolve, ) -> anyhow::Result> { @@ -422,12 +425,14 @@ fn collect_stub_resources<'a>( .collect::>() }); + let resource_name = typ + .name + .as_ref() + .ok_or(anyhow!("Resource type has no name"))? + .clone(); + interfaces.push(InterfaceStub { - name: typ - .name - .as_ref() - .ok_or(anyhow!("Resource type has no name"))? - .clone(), + name: format!("{owner_interface}/{resource_name}"), functions, imports, global: false, From 6918e3e7ec0df5e8631ffbb71b00e26ac60bc1f1 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 22 Feb 2024 17:27:56 +0100 Subject: [PATCH 4/5] Fixes --- wasm-rpc-stubgen/src/rust.rs | 31 +++++++++++++++++++++++++------ wasm-rpc-stubgen/src/stub.rs | 6 +++++- wasm-rpc/src/wasmtime.rs | 2 +- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/wasm-rpc-stubgen/src/rust.rs b/wasm-rpc-stubgen/src/rust.rs index b1114617..486e4bd8 100644 --- a/wasm-rpc-stubgen/src/rust.rs +++ b/wasm-rpc-stubgen/src/rust.rs @@ -85,7 +85,10 @@ pub fn generate_stub_source(def: &StubDefinition) -> anyhow::Result<()> { if interface.global { None } else { - Some(&interface.name) + match &interface.owner_interface { + Some(owner) => Some(format!("{owner}/{}", &interface.name)), + None => Some(interface.name.clone()), + } }, if interface.is_resource() { FunctionMode::Method @@ -102,7 +105,10 @@ pub fn generate_stub_source(def: &StubDefinition) -> anyhow::Result<()> { if interface.global { None } else { - Some(&interface.name) + match &interface.owner_interface { + Some(owner) => Some(format!("{owner}/{}", &interface.name)), + None => Some(interface.name.clone()), + } }, FunctionMode::Static, )?); @@ -123,7 +129,11 @@ pub fn generate_stub_source(def: &StubDefinition) -> anyhow::Result<()> { generate_function_stub_source( def, &constructor_stub, - Some(&interface.name), + Some(format!( + "{}/{}", + interface.owner_interface.clone().unwrap_or_default(), + &interface.name + )), FunctionMode::Constructor, )? } else { @@ -146,7 +156,15 @@ pub fn generate_stub_source(def: &StubDefinition) -> anyhow::Result<()> { }); if interface.is_resource() { - let remote_function_name = get_remote_function_name(def, "drop", Some(&interface.name)); + let remote_function_name = get_remote_function_name( + def, + "drop", + Some(&format!( + "{}/{}", + interface.owner_interface.clone().unwrap_or_default(), + &interface.name + )), + ); interface_impls.push(quote! { impl Drop for #interface_name { fn drop(&mut self) { @@ -198,7 +216,7 @@ enum FunctionMode { fn generate_function_stub_source( def: &StubDefinition, function: &FunctionStub, - interface_name: Option<&String>, + interface_name: Option, mode: FunctionMode, ) -> anyhow::Result { let function_name = Ident::new(&to_rust_ident(&function.name), Span::call_site()); @@ -301,7 +319,8 @@ fn generate_function_stub_source( } } - let remote_function_name = get_remote_function_name(def, &function.name, interface_name); + let remote_function_name = + get_remote_function_name(def, &function.name, interface_name.as_ref()); let rpc = match mode { FunctionMode::Static => { diff --git a/wasm-rpc-stubgen/src/stub.rs b/wasm-rpc-stubgen/src/stub.rs index aed89acb..e47bb6e9 100644 --- a/wasm-rpc-stubgen/src/stub.rs +++ b/wasm-rpc-stubgen/src/stub.rs @@ -129,6 +129,7 @@ pub struct InterfaceStub { pub static_functions: Vec, pub imports: Vec, pub global: bool, + pub owner_interface: Option, } impl InterfaceStub { @@ -302,6 +303,7 @@ fn collect_stub_interfaces(resolve: &Resolve, world: &World) -> anyhow::Result anyhow::Result( .clone(); interfaces.push(InterfaceStub { - name: format!("{owner_interface}/{resource_name}"), + name: resource_name, functions, imports, global: false, constructor_params, static_functions, + owner_interface: Some(owner_interface.to_string()), }); } TypeOwner::None => {} diff --git a/wasm-rpc/src/wasmtime.rs b/wasm-rpc/src/wasmtime.rs index 02ae92d2..6dcdd3b8 100644 --- a/wasm-rpc/src/wasmtime.rs +++ b/wasm-rpc/src/wasmtime.rs @@ -376,7 +376,7 @@ pub fn encode_output( Ok(Value::Flags(encoded_value)) } Val::Resource(resource) => { - let id = resource_store.add(resource.clone()); + let id = resource_store.add(*resource); Ok(Value::Handle { uri: resource_store.self_uri(), resource_id: id, From 8017fcfbb03abc56793617a16c16f837d8c8229d Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 22 Feb 2024 17:37:13 +0100 Subject: [PATCH 5/5] Fix result tuple extraction --- wasm-rpc-stubgen/src/rust.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm-rpc-stubgen/src/rust.rs b/wasm-rpc-stubgen/src/rust.rs index 486e4bd8..c432abdf 100644 --- a/wasm-rpc-stubgen/src/rust.rs +++ b/wasm-rpc-stubgen/src/rust.rs @@ -288,7 +288,7 @@ fn generate_function_stub_source( output_values.push(extract_from_wit_value( typ, &def.resolve, - quote! { result }, + quote! { result.tuple_element(0).expect("tuple not found") }, )?); } FunctionResultStub::Multi(params) => { @@ -303,7 +303,7 @@ fn generate_function_stub_source( FunctionResultStub::SelfType if mode == FunctionMode::Constructor => { output_values.push(quote! { { - let (uri, id) = result.handle().expect("handle not found"); + let (uri, id) = result.tuple_element(0).expect("tuple not found").handle().expect("handle not found"); Self { rpc, id,