diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index 52ef64bcc2b2..c61ce3a74f0a 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -100,6 +100,18 @@ impl<'a> TrampolineCompiler<'a> { Trampoline::ResourceNew(ty) => self.translate_resource_new(*ty), Trampoline::ResourceRep(ty) => self.translate_resource_rep(*ty), Trampoline::ResourceDrop(ty) => self.translate_resource_drop(*ty), + Trampoline::ResourceTransferOwn => { + self.translate_resource_libcall(host::resource_transfer_own) + } + Trampoline::ResourceTransferBorrow => { + self.translate_resource_libcall(host::resource_transfer_borrow) + } + Trampoline::ResourceEnterCall => { + self.translate_resource_libcall(host::resource_enter_call) + } + Trampoline::ResourceExitCall => { + self.translate_resource_libcall(host::resource_exit_call) + } } } @@ -496,6 +508,41 @@ impl<'a> TrampolineCompiler<'a> { self.builder.seal_block(return_block); } + /// Invokes a host libcall and returns the result. + /// + /// Only intended for simple trampolines and effectively acts as a bridge + /// from the wasm abi to host. + fn translate_resource_libcall( + &mut self, + get_libcall: fn(&dyn TargetIsa, &mut ir::Function) -> (ir::SigRef, u32), + ) { + match self.abi { + Abi::Wasm => {} + + // These trampolines can only actually be called by Wasm, so + // let's assert that here. + Abi::Native | Abi::Array => { + self.builder + .ins() + .trap(ir::TrapCode::User(crate::DEBUG_ASSERT_TRAP_CODE)); + return; + } + } + + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + host_args.extend(args[2..].iter().copied()); + let (host_sig, offset) = get_libcall(self.isa, &mut self.builder.func); + let host_fn = self.load_libcall(vmctx, offset); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + let results = self.builder.func.dfg.inst_results(call).to_vec(); + self.builder.ins().return_(&results); + } + /// Loads a host function pointer for a libcall stored at the `offset` /// provided in the libcalls array. /// diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs index 880ee9bacae8..b5537adc0016 100644 --- a/crates/environ/src/component.rs +++ b/crates/environ/src/component.rs @@ -85,6 +85,11 @@ macro_rules! foreach_builtin_component_function { // is encoded as a 64-bit integer where the low bit is Some/None // and bits 1-33 are the payload. resource_drop(vmctx: vmctx, resource: u32, idx: u32) -> u64; + + resource_transfer_own(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u32; + resource_transfer_borrow(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u32; + resource_enter_call(vmctx: vmctx); + resource_exit_call(vmctx: vmctx); } }; } diff --git a/crates/environ/src/component/dfg.rs b/crates/environ/src/component/dfg.rs index f81135e0d982..2fab963234b4 100644 --- a/crates/environ/src/component/dfg.rs +++ b/crates/environ/src/component/dfg.rs @@ -48,7 +48,7 @@ pub struct ComponentDfg { /// All trampolines and their type signature which will need to get /// compiled by Cranelift. - pub trampolines: PrimaryMap, + pub trampolines: Intern, /// Know reallocation functions which are used by `lowerings` (e.g. will be /// used by the host) @@ -238,6 +238,7 @@ impl CoreExport { } /// Same as `info::Trampoline` +#[derive(Clone, PartialEq, Eq, Hash)] #[allow(missing_docs)] pub enum Trampoline { LowerImport { @@ -256,6 +257,10 @@ pub enum Trampoline { ResourceNew(TypeResourceTableIndex), ResourceRep(TypeResourceTableIndex), ResourceDrop(TypeResourceTableIndex), + ResourceTransferOwn, + ResourceTransferBorrow, + ResourceEnterCall, + ResourceExitCall, } /// Same as `info::CanonicalOptions` @@ -581,6 +586,10 @@ impl LinearizeDfg<'_> { Trampoline::ResourceNew(ty) => info::Trampoline::ResourceNew(*ty), Trampoline::ResourceDrop(ty) => info::Trampoline::ResourceDrop(*ty), Trampoline::ResourceRep(ty) => info::Trampoline::ResourceRep(*ty), + Trampoline::ResourceTransferOwn => info::Trampoline::ResourceTransferOwn, + Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow, + Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall, + Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall, }; let i1 = self.trampolines.push(*signature); let i2 = self.trampoline_defs.push(trampoline); diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index 583d81e33f98..8a1cf70c8c52 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -533,6 +533,24 @@ pub enum Trampoline { /// Same as `ResourceNew`, but for the `resource.drop` intrinsic. ResourceDrop(TypeResourceTableIndex), + + /// An intrinsic used by FACT-generated modules which will transfer an owned + /// resource from one table to another. Used in component-to-component + /// adapter trampolines. + ResourceTransferOwn, + + /// Same as `ResourceTransferOwn` but for borrows. + ResourceTransferBorrow, + + /// An intrinsic used by FACT-generated modules which indicates that a call + /// is being entered and resource-related metadata needs to be configured. + /// + /// Note that this is currently only invoked when borrowed resources are + /// detected, otherwise this is "optimized out". + ResourceEnterCall, + + /// Same as `ResourceEnterCall` except for when exiting a call. + ResourceExitCall, } impl Trampoline { @@ -556,6 +574,10 @@ impl Trampoline { ResourceNew(i) => format!("component-resource-new[{}]", i.as_u32()), ResourceRep(i) => format!("component-resource-rep[{}]", i.as_u32()), ResourceDrop(i) => format!("component-resource-drop[{}]", i.as_u32()), + ResourceTransferOwn => format!("component-resource-transfer-own"), + ResourceTransferBorrow => format!("component-resource-transfer-borrow"), + ResourceEnterCall => format!("component-resource-enter-call"), + ResourceExitCall => format!("component-resource-exit-call"), } } } diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs index 91d1d6ab3985..e182d599708c 100644 --- a/crates/environ/src/component/translate/adapt.rs +++ b/crates/environ/src/component/translate/adapt.rs @@ -257,6 +257,11 @@ fn fact_import_to_core_def( import: &fact::Import, ty: EntityType, ) -> dfg::CoreDef { + let mut simple_intrinsic = |trampoline: dfg::Trampoline| { + let signature = ty.unwrap_func(); + let index = dfg.trampolines.push((signature, trampoline)); + dfg::CoreDef::Trampoline(index) + }; match import { fact::Import::CoreDef(def) => def.clone(), fact::Import::Transcode { @@ -278,10 +283,7 @@ fn fact_import_to_core_def( let from = dfg.memories.push(unwrap_memory(from)); let to = dfg.memories.push(unwrap_memory(to)); - let signature = match ty { - EntityType::Function(signature) => signature, - _ => unreachable!(), - }; + let signature = ty.unwrap_func(); let index = dfg.trampolines.push(( signature, dfg::Trampoline::Transcoder { @@ -294,6 +296,12 @@ fn fact_import_to_core_def( )); dfg::CoreDef::Trampoline(index) } + fact::Import::ResourceTransferOwn => simple_intrinsic(dfg::Trampoline::ResourceTransferOwn), + fact::Import::ResourceTransferBorrow => { + simple_intrinsic(dfg::Trampoline::ResourceTransferBorrow) + } + fact::Import::ResourceEnterCall => simple_intrinsic(dfg::Trampoline::ResourceEnterCall), + fact::Import::ResourceExitCall => simple_intrinsic(dfg::Trampoline::ResourceExitCall), } } diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index 38681ea1e731..ed262379e8df 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -867,6 +867,12 @@ impl ComponentTypesBuilder { self.type_information(ty).flat.as_flat_types() } + /// Returns whether the type specified contains any borrowed resources + /// within it. + pub fn ty_contains_borrow_resource(&self, ty: &InterfaceType) -> bool { + self.type_information(ty).has_borrow + } + fn type_information(&self, ty: &InterfaceType) -> &TypeInformation { match ty { InterfaceType::U8 @@ -877,11 +883,18 @@ impl ComponentTypesBuilder { | InterfaceType::U32 | InterfaceType::S32 | InterfaceType::Char - | InterfaceType::Own(_) - | InterfaceType::Borrow(_) => { + | InterfaceType::Own(_) => { static INFO: TypeInformation = TypeInformation::primitive(FlatType::I32); &INFO } + InterfaceType::Borrow(_) => { + static INFO: TypeInformation = { + let mut info = TypeInformation::primitive(FlatType::I32); + info.has_borrow = true; + info + }; + &INFO + } InterfaceType::U64 | InterfaceType::S64 => { static INFO: TypeInformation = TypeInformation::primitive(FlatType::I64); &INFO @@ -1711,6 +1724,7 @@ struct TypeInformationCache { struct TypeInformation { depth: u32, flat: FlatTypesStorage, + has_borrow: bool, } impl TypeInformation { @@ -1718,6 +1732,7 @@ impl TypeInformation { TypeInformation { depth: 0, flat: FlatTypesStorage::new(), + has_borrow: false, } } @@ -1747,6 +1762,7 @@ impl TypeInformation { self.depth = 1; for info in types { self.depth = self.depth.max(1 + info.depth); + self.has_borrow = self.has_borrow || info.has_borrow; match info.flat.as_flat_types() { Some(types) => { for (t32, t64) in types.memory32.iter().zip(types.memory64) { @@ -1789,6 +1805,7 @@ impl TypeInformation { None => continue, }; self.depth = self.depth.max(1 + info.depth); + self.has_borrow = self.has_borrow || info.has_borrow; // If this variant is already unrepresentable in a flat // representation then this can be skipped. @@ -1898,5 +1915,6 @@ impl TypeInformation { *self = TypeInformation::string(); let info = types.type_information(&ty.element); self.depth += info.depth; + self.has_borrow = info.has_borrow; } } diff --git a/crates/environ/src/fact.rs b/crates/environ/src/fact.rs index bf9b2ffcedab..4d35ee5ea602 100644 --- a/crates/environ/src/fact.rs +++ b/crates/environ/src/fact.rs @@ -60,6 +60,12 @@ pub struct Module<'a> { /// Intern'd transcoders and what index they were assigned. imported_transcoders: HashMap, + /// Cached versions of imported trampolines for working with resources. + imported_resource_transfer_own: Option, + imported_resource_transfer_borrow: Option, + imported_resource_enter_call: Option, + imported_resource_exit_call: Option, + // Current status of index spaces from the imports generated so far. imported_funcs: PrimaryMap>, imported_memories: PrimaryMap, @@ -178,6 +184,10 @@ impl<'a> Module<'a> { funcs: PrimaryMap::new(), helper_funcs: HashMap::new(), helper_worklist: Vec::new(), + imported_resource_transfer_own: None, + imported_resource_transfer_borrow: None, + imported_resource_enter_call: None, + imported_resource_exit_call: None, } } @@ -359,6 +369,72 @@ impl<'a> Module<'a> { }) } + fn import_simple( + &mut self, + module: &str, + name: &str, + params: &[ValType], + results: &[ValType], + import: Import, + get: impl Fn(&mut Self) -> &mut Option, + ) -> FuncIndex { + if let Some(idx) = get(self) { + return *idx; + } + let ty = self.core_types.function(params, results); + let ty = EntityType::Function(ty); + self.core_imports.import(module, name, ty); + + self.imports.push(import); + let idx = self.imported_funcs.push(None); + *get(self) = Some(idx); + idx + } + + fn import_resource_transfer_own(&mut self) -> FuncIndex { + self.import_simple( + "resource", + "transfer-own", + &[ValType::I32, ValType::I32, ValType::I32], + &[ValType::I32], + Import::ResourceTransferOwn, + |me| &mut me.imported_resource_transfer_own, + ) + } + + fn import_resource_transfer_borrow(&mut self) -> FuncIndex { + self.import_simple( + "resource", + "transfer-borrow", + &[ValType::I32, ValType::I32, ValType::I32], + &[ValType::I32], + Import::ResourceTransferBorrow, + |me| &mut me.imported_resource_transfer_borrow, + ) + } + + fn import_resource_enter_call(&mut self) -> FuncIndex { + self.import_simple( + "resource", + "enter-call", + &[], + &[], + Import::ResourceEnterCall, + |me| &mut me.imported_resource_enter_call, + ) + } + + fn import_resource_exit_call(&mut self) -> FuncIndex { + self.import_simple( + "resource", + "exit-call", + &[], + &[], + Import::ResourceExitCall, + |me| &mut me.imported_resource_exit_call, + ) + } + fn translate_helper(&mut self, helper: Helper) -> FunctionId { *self.helper_funcs.entry(helper).or_insert_with(|| { // Generate a fresh `Function` with a unique id for what we're about to @@ -470,6 +546,15 @@ pub enum Import { /// Whether or not `to` is a 64-bit memory to64: bool, }, + /// Transfers an owned resource from one table to another. + ResourceTransferOwn, + /// Transfers a borrowed resource from one table to another. + ResourceTransferBorrow, + /// Sets up entry metadata for a borrow resources when a call starts. + ResourceEnterCall, + /// Tears down a previous entry and handles checking borrow-related + /// metadata. + ResourceExitCall, } impl Options { diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index adc94bd588e4..07660d4d0d61 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -115,4 +115,21 @@ impl ComponentTypesBuilder { (abi.size32, abi.align32) } } + + /// Tests whether the type signature for `options` contains a borrowed + /// resource anywhere. + pub(super) fn contains_borrow_resource(&self, options: &AdapterOptions) -> bool { + let ty = &self[options.ty]; + + // Only parameters need to be checked since results should never have + // borrowed resources. + debug_assert!(!self[ty.results] + .types + .iter() + .any(|t| self.ty_contains_borrow_resource(t))); + self[ty.params] + .types + .iter() + .any(|t| self.ty_contains_borrow_resource(t)) + } } diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 9b7fd6e1806f..000d843e2ef8 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -70,6 +70,12 @@ struct Compiler<'a, 'b> { /// to be a heuristic to split up the main function into theoretically /// reusable portions. fuel: usize, + + /// Indicates whether an "enter call" should be emitted in the generated + /// function with a call to `Resource{Enter,Exit}Call` at the beginning and + /// end of the function for tracking of information related to borrowed + /// resources. + emit_resource_call: bool, } pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { @@ -81,6 +87,17 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { let result = module .funcs .push(Function::new(Some(adapter.name.clone()), ty)); + + // If this type signature contains any borrowed resources then invocations + // of enter/exit call for resource-related metadata tracking must be used. + // It shouldn't matter whether the lower/lift signature is used here as both + // should return the same answer. + let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower); + assert_eq!( + emit_resource_call, + module.types.contains_borrow_resource(&adapter.lift) + ); + Compiler { types: module.types, module, @@ -90,6 +107,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { traps: Vec::new(), result, fuel: INITIAL_FUEL, + emit_resource_call, } .compile_adapter(adapter, &lower_sig, &lift_sig) } @@ -165,6 +183,9 @@ pub(super) fn compile_helper(module: &mut Module<'_>, result: FunctionId, helper traps: Vec::new(), result, fuel: INITIAL_FUEL, + // This is a helper function and only the top-level function is + // responsible for emitting these intrinsic calls. + emit_resource_call: false, }; compiler.translate(&helper.src.ty, &src, &helper.dst.ty, &dst); compiler.finish(); @@ -245,6 +266,11 @@ impl Compiler<'_, '_> { ); } + if self.emit_resource_call { + let enter = self.module.import_resource_enter_call(); + self.instruction(Call(enter.as_u32())); + } + // Perform the translation of arguments. Note that `FLAG_MAY_LEAVE` is // cleared around this invocation for the callee as per the // `canon_lift` definition in the spec. Additionally note that the @@ -308,6 +334,11 @@ impl Compiler<'_, '_> { self.free_temp_local(tmp); } + if self.emit_resource_call { + let exit = self.module.import_resource_exit_call(); + self.instruction(Call(exit.as_u32())); + } + self.finish() } @@ -2460,12 +2491,11 @@ impl Compiler<'_, '_> { dst: &Destination, ) { let dst_ty = match dst_ty { - InterfaceType::Own(t) => t, + InterfaceType::Own(t) => *t, _ => panic!("expected an `Own`"), }; - - let _ = (src_ty, src, dst_ty, dst); - todo!("TODO: #6696"); + let transfer = self.module.import_resource_transfer_own(); + self.translate_resource(src_ty, src, dst_ty, dst, transfer); } fn translate_borrow( @@ -2476,12 +2506,41 @@ impl Compiler<'_, '_> { dst: &Destination, ) { let dst_ty = match dst_ty { - InterfaceType::Borrow(t) => t, + InterfaceType::Borrow(t) => *t, _ => panic!("expected an `Borrow`"), }; - let _ = (src_ty, src, dst_ty, dst); - todo!("TODO: #6696"); + let transfer = self.module.import_resource_transfer_borrow(); + self.translate_resource(src_ty, src, dst_ty, dst, transfer); + } + + /// Translates the index `src`, which resides in the table `src_ty`, into + /// and index within `dst_ty` and is stored at `dst`. + /// + /// Actual translation of the index happens in a wasmtime libcall, which a + /// cranelift-generated trampoline to satisfy this import will call. The + /// `transfer` function is an imported function which takes the src, src_ty, + /// and dst_ty, and returns the dst index. + fn translate_resource( + &mut self, + src_ty: TypeResourceTableIndex, + src: &Source<'_>, + dst_ty: TypeResourceTableIndex, + dst: &Destination, + transfer: FuncIndex, + ) { + self.push_dst_addr(dst); + match src { + Source::Memory(mem) => self.i32_load(mem), + Source::Stack(stack) => self.stack_get(stack, ValType::I32), + } + self.instruction(I32Const(src_ty.as_u32() as i32)); + self.instruction(I32Const(dst_ty.as_u32() as i32)); + self.instruction(Call(transfer.as_u32())); + match dst { + Destination::Memory(mem) => self.i32_store(mem), + Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32), + } } fn trap_if_not_flag(&mut self, flags_global: GlobalIndex, flag_to_test: i32, trap: Trap) { diff --git a/crates/runtime/src/component.rs b/crates/runtime/src/component.rs index 2b4d41f21a94..39f9f61eaef3 100644 --- a/crates/runtime/src/component.rs +++ b/crates/runtime/src/component.rs @@ -25,8 +25,8 @@ use wasmtime_environ::{HostPtr, PrimaryMap}; const INVALID_PTR: usize = 0xdead_dead_beef_beef_u64 as usize; +mod libcalls; mod resources; -mod transcode; pub use self::resources::{CallContexts, ResourceTable, ResourceTables}; @@ -440,8 +440,7 @@ impl ComponentInstance { unsafe fn initialize_vmctx(&mut self, store: *mut dyn Store) { *self.vmctx_plus_offset_mut(self.offsets.magic()) = VMCOMPONENT_MAGIC; - *self.vmctx_plus_offset_mut(self.offsets.libcalls()) = - &transcode::VMComponentLibcalls::INIT; + *self.vmctx_plus_offset_mut(self.offsets.libcalls()) = &libcalls::VMComponentLibcalls::INIT; *self.vmctx_plus_offset_mut(self.offsets.store()) = store; *self.vmctx_plus_offset_mut(self.offsets.limits()) = (*store).vmruntime_limits(); @@ -585,6 +584,48 @@ impl ComponentInstance { }); (dtor, flags) } + + pub(crate) fn resource_transfer_own( + &mut self, + idx: u32, + src: TypeResourceTableIndex, + dst: TypeResourceTableIndex, + ) -> Result { + let mut tables = self.resource_tables(); + let rep = tables.resource_lift_own(Some(src), idx)?; + Ok(tables.resource_lower_own(Some(dst), rep)) + } + + pub(crate) fn resource_transfer_borrow( + &mut self, + idx: u32, + src: TypeResourceTableIndex, + dst: TypeResourceTableIndex, + ) -> Result { + let dst_owns_resource = self.resource_owned_by_own_instance(dst); + let mut tables = self.resource_tables(); + let rep = tables.resource_lift_borrow(Some(src), idx)?; + // Implement `lower_borrow`'s special case here where if a borrow's + // resource type is owned by `dst` then the destination receives the + // representation directly rather than a handle to the representation. + // + // This can perhaps become a different libcall in the future to avoid + // this check at runtime since we know at compile time whether the + // destination type owns the resource, but that's left as a future + // refactoring if truly necessary. + if dst_owns_resource { + return Ok(rep); + } + Ok(tables.resource_lower_borrow(Some(dst), rep)) + } + + pub(crate) fn resource_enter_call(&mut self) { + self.resource_tables().enter_call() + } + + pub(crate) fn resource_exit_call(&mut self) -> Result<()> { + self.resource_tables().exit_call() + } } impl VMComponentContext { diff --git a/crates/runtime/src/component/transcode.rs b/crates/runtime/src/component/libcalls.rs similarity index 93% rename from crates/runtime/src/component/transcode.rs rename to crates/runtime/src/component/libcalls.rs index 7aebc5bc7e22..920a505339a3 100644 --- a/crates/runtime/src/component/transcode.rs +++ b/crates/runtime/src/component/libcalls.rs @@ -47,7 +47,7 @@ macro_rules! define_builtins { /// An array that stores addresses of builtin functions. We translate code /// to use indirect calls. This way, we don't have to patch the code. #[repr(C)] - pub struct VMComponentBuiltins { + struct VMComponentBuiltins { $( $name: unsafe extern "C" fn( $(signature!(@ty $param),)* @@ -56,7 +56,7 @@ macro_rules! define_builtins { } impl VMComponentBuiltins { - pub const INIT: VMComponentBuiltins = VMComponentBuiltins { + const INIT: VMComponentBuiltins = VMComponentBuiltins { $($name: trampolines::$name,)* }; } @@ -84,7 +84,7 @@ macro_rules! define_transcoders { /// An array that stores addresses of builtin functions. We translate code /// to use indirect calls. This way, we don't have to patch the code. #[repr(C)] - pub struct VMBuiltinTranscodeArray { + struct VMBuiltinTranscodeArray { $( $name: unsafe extern "C" fn( $(signature!(@ty $param),)* @@ -93,7 +93,7 @@ macro_rules! define_transcoders { } impl VMBuiltinTranscodeArray { - pub const INIT: VMBuiltinTranscodeArray = VMBuiltinTranscodeArray { + const INIT: VMBuiltinTranscodeArray = VMBuiltinTranscodeArray { $($name: trampolines::$name,)* }; } @@ -538,3 +538,37 @@ unsafe fn resource_drop(vmctx: *mut VMComponentContext, resource: u32, idx: u32) }) }) } + +unsafe fn resource_transfer_own( + vmctx: *mut VMComponentContext, + src_idx: u32, + src_table: u32, + dst_table: u32, +) -> Result { + let src_table = TypeResourceTableIndex::from_u32(src_table); + let dst_table = TypeResourceTableIndex::from_u32(dst_table); + ComponentInstance::from_vmctx(vmctx, |instance| { + instance.resource_transfer_own(src_idx, src_table, dst_table) + }) +} + +unsafe fn resource_transfer_borrow( + vmctx: *mut VMComponentContext, + src_idx: u32, + src_table: u32, + dst_table: u32, +) -> Result { + let src_table = TypeResourceTableIndex::from_u32(src_table); + let dst_table = TypeResourceTableIndex::from_u32(dst_table); + ComponentInstance::from_vmctx(vmctx, |instance| { + instance.resource_transfer_borrow(src_idx, src_table, dst_table) + }) +} + +unsafe fn resource_enter_call(vmctx: *mut VMComponentContext) -> Result<()> { + ComponentInstance::from_vmctx(vmctx, |instance| Ok(instance.resource_enter_call())) +} + +unsafe fn resource_exit_call(vmctx: *mut VMComponentContext) -> Result<()> { + ComponentInstance::from_vmctx(vmctx, |instance| instance.resource_exit_call()) +} diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 0fbd349400de..320c6e479d99 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -294,6 +294,48 @@ pub enum EntityType { Function(SignatureIndex), } +impl EntityType { + /// Assert that this entity is a global + pub fn unwrap_global(&self) -> &Global { + match self { + EntityType::Global(g) => g, + _ => panic!("not a global"), + } + } + + /// Assert that this entity is a memory + pub fn unwrap_memory(&self) -> &Memory { + match self { + EntityType::Memory(g) => g, + _ => panic!("not a memory"), + } + } + + /// Assert that this entity is a tag + pub fn unwrap_tag(&self) -> &Tag { + match self { + EntityType::Tag(g) => g, + _ => panic!("not a tag"), + } + } + + /// Assert that this entity is a table + pub fn unwrap_table(&self) -> &Table { + match self { + EntityType::Table(g) => g, + _ => panic!("not a table"), + } + } + + /// Assert that this entity is a function + pub fn unwrap_func(&self) -> SignatureIndex { + match self { + EntityType::Function(g) => *g, + _ => panic!("not a func"), + } + } +} + /// A WebAssembly global. /// /// Note that we record both the original Wasm type and the Cranelift IR type diff --git a/crates/wasmtime/src/component/instance.rs b/crates/wasmtime/src/component/instance.rs index e824539ff191..6a664889d904 100644 --- a/crates/wasmtime/src/component/instance.rs +++ b/crates/wasmtime/src/component/instance.rs @@ -487,14 +487,10 @@ impl<'a> Instantiator<'a> { // there's no guarantee that there exists a trampoline for `f` so this // can't fall through to the case below if let wasmtime_runtime::Export::Function(f) = &export { - match expected { - EntityType::Function(expected) => { - let actual = unsafe { f.func_ref.as_ref().type_index }; - assert_eq!(module.signatures().shared_signature(expected), Some(actual)); - return; - } - _ => panic!("function not expected"), - } + let expected = expected.unwrap_func(); + let actual = unsafe { f.func_ref.as_ref().type_index }; + assert_eq!(module.signatures().shared_signature(expected), Some(actual)); + return; } let val = unsafe { crate::Extern::from_wasmtime_export(export, store) }; diff --git a/tests/misc_testsuite/component-model/resources.wast b/tests/misc_testsuite/component-model/resources.wast index 3d521beffa2a..decb9554e85e 100644 --- a/tests/misc_testsuite/component-model/resources.wast +++ b/tests/misc_testsuite/component-model/resources.wast @@ -956,3 +956,96 @@ ) (assert_trap (invoke "f") "unknown handle index 0") + +(component + (component $A + (type $t' (resource (rep i32))) + (export $t "t" (type $t')) + + (core func $ctor (canon resource.new $t)) + (core func $dtor (canon resource.drop $t)) + (core func $rep (canon resource.rep $t)) + + (core module $m + (import "" "dtor" (func $dtor (param i32))) + (import "" "rep" (func $rep (param i32) (result i32))) + + (func (export "[method]t.assert") (param i32 i32) + (if (i32.ne (local.get 0) (local.get 1)) (unreachable)) + ) + (func (export "[static]t.assert-own") (param i32 i32) + (if (i32.ne (call $rep (local.get 0)) (local.get 1)) (unreachable)) + (call $dtor (local.get 0)) + ) + ) + (core instance $i (instantiate $m + (with "" (instance + (export "dtor" (func $dtor)) + (export "rep" (func $rep)) + )) + )) + (func (export "[constructor]t") (param "x" u32) (result (own $t)) + (canon lift (core func $ctor))) + (func (export "[method]t.assert") (param "self" (borrow $t)) (param "x" u32) + (canon lift (core func $i "[method]t.assert"))) + (func (export "[static]t.assert-own") (param "self" (own $t)) (param "x" u32) + (canon lift (core func $i "[static]t.assert-own"))) + ) + (instance $a (instantiate $A)) + + (component $B + (import "a" (instance $i + (export $t "t" (type (sub resource))) + (export "[constructor]t" (func (param "x" u32) (result (own $t)))) + (export "[method]t.assert" (func (param "self" (borrow $t)) (param "x" u32))) + (export "[static]t.assert-own" (func (param "self" (own $t)) (param "x" u32))) + )) + + (alias export $i "t" (type $t)) + (alias export $i "[constructor]t" (func $ctor)) + (alias export $i "[method]t.assert" (func $assert-borrow)) + (alias export $i "[static]t.assert-own" (func $assert-own)) + + (core func $ctor (canon lower (func $ctor))) + (core func $dtor (canon resource.drop $t)) + (core func $assert-own (canon lower (func $assert-own))) + (core func $assert-borrow (canon lower (func $assert-borrow))) + + (core module $m + (import "" "ctor" (func $ctor (param i32) (result i32))) + (import "" "dtor" (func $dtor (param i32))) + (import "" "assert-own" (func $assert-own (param i32 i32))) + (import "" "assert-borrow" (func $assert-borrow (param i32 i32))) + + (func (export "f") + (local $r1 i32) + (local $r2 i32) + + (local.set $r1 (call $ctor (i32.const 100))) + (local.set $r2 (call $ctor (i32.const 200))) + + (if (i32.ne (local.get $r1) (i32.const 0)) (unreachable)) + (if (i32.ne (local.get $r2) (i32.const 1)) (unreachable)) + + (call $assert-borrow (local.get $r2) (i32.const 200)) + (call $assert-borrow (local.get $r1) (i32.const 100)) + + (call $assert-own (local.get $r2) (i32.const 200)) + (call $dtor (local.get $r1)) + ) + ) + (core instance $i (instantiate $m + (with "" (instance + (export "ctor" (func $ctor)) + (export "dtor" (func $dtor)) + (export "assert-own" (func $assert-own)) + (export "assert-borrow" (func $assert-borrow)) + )) + )) + (func (export "f") (canon lift (core func $i "f"))) + ) + (instance $b (instantiate $B (with "a" (instance $a)))) + (export "f" (func $b "f")) +) + +(assert_return (invoke "f"))