diff --git a/Cargo.lock b/Cargo.lock index 0f091e86d0..9c71d703c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3076,6 +3076,7 @@ dependencies = [ "libtest-mimic", "tempfile", "wasm-encoder 0.240.0", + "wasmparser 0.240.0", "wit-component 0.240.0", "wit-parser 0.240.0", "wit-smith", diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs index 6782a99fd2..5022f7f388 100644 --- a/crates/wasm-compose/src/encoding.rs +++ b/crates/wasm-compose/src/encoding.rs @@ -482,7 +482,7 @@ impl<'a> TypeEncoder<'a> { let index = state.cur.encodable.type_count(); let mut f = state.cur.encodable.ty().function(); - f.params(params).result(result); + f.async_(ty.async_).params(params).result(result); index } diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs index 980d7701ab..f9887e0e7e 100644 --- a/crates/wasm-encoder/src/component/types.rs +++ b/crates/wasm-encoder/src/component/types.rs @@ -353,6 +353,7 @@ impl Encode for InstanceType { /// Used to encode component function types. #[derive(Debug)] pub struct ComponentFuncTypeEncoder<'a> { + async_encoded: bool, params_encoded: bool, results_encoded: bool, sink: &'a mut Vec, @@ -360,14 +361,36 @@ pub struct ComponentFuncTypeEncoder<'a> { impl<'a> ComponentFuncTypeEncoder<'a> { fn new(sink: &'a mut Vec) -> Self { - sink.push(0x40); Self { + async_encoded: false, params_encoded: false, results_encoded: false, sink, } } + /// Indicates whether this is an `async` function or not. + /// + /// If this function is not invoked then the function type will not be + /// `async`. + /// + /// # Panics + /// + /// This method will panic if parameters or results have already been + /// encoded. + pub fn async_(&mut self, is_async: bool) -> &mut Self { + assert!(!self.params_encoded); + assert!(!self.results_encoded); + assert!(!self.async_encoded); + self.async_encoded = true; + if is_async { + self.sink.push(0x43); + } else { + self.sink.push(0x40); + } + self + } + /// Defines named parameters. /// /// Parameters must be defined before defining results. @@ -383,6 +406,9 @@ impl<'a> ComponentFuncTypeEncoder<'a> { T: Into, { assert!(!self.params_encoded); + if !self.async_encoded { + self.async_(false); + } self.params_encoded = true; let params = params.into_iter(); params.len().encode(self.sink); @@ -402,6 +428,7 @@ impl<'a> ComponentFuncTypeEncoder<'a> { /// This method will panic if the function is called twice, called before /// the `params` method, or called in addition to the `results` method. pub fn result(&mut self, ty: Option) -> &mut Self { + assert!(self.async_encoded); assert!(self.params_encoded); assert!(!self.results_encoded); self.results_encoded = true; diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs index 55990698ec..e6d5afe5b6 100644 --- a/crates/wasm-encoder/src/reencode/component.rs +++ b/crates/wasm-encoder/src/reencode/component.rs @@ -731,6 +731,7 @@ pub mod component_utils { mut func: crate::ComponentFuncTypeEncoder<'_>, ty: wasmparser::ComponentFuncType<'_>, ) -> Result<(), Error> { + func.async_(ty.async_); func.params( Vec::from(ty.params) .into_iter() diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs index 5a9dfb24bc..b87c26da92 100644 --- a/crates/wasmparser/src/readers/component/types.rs +++ b/crates/wasmparser/src/readers/component/types.rs @@ -282,12 +282,16 @@ impl<'a> FromReader<'a> for ComponentType<'a> { b => return reader.invalid_leading_byte(b, "resource destructor"), }, }, - 0x40 => { + byte @ (0x40 | 0x43) => { let params = reader .read_iter(MAX_WASM_FUNCTION_PARAMS, "component function parameters")? .collect::>()?; let result = read_resultlist(reader)?; - ComponentType::Func(ComponentFuncType { params, result }) + ComponentType::Func(ComponentFuncType { + async_: byte == 0x43, + params, + result, + }) } 0x41 => ComponentType::Component( reader @@ -387,6 +391,8 @@ impl<'a> FromReader<'a> for InstanceTypeDeclaration<'a> { /// Represents a type of a function in a WebAssembly component. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ComponentFuncType<'a> { + /// Whether or not this is an async function. + pub async_: bool, /// The function parameters. pub params: Box<[(&'a str, ComponentValType)]>, /// The function result. diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 71cee60489..9dfe0a0ef1 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1447,6 +1447,7 @@ impl ComponentState { } let func_ty = ComponentFuncType { + async_: false, info: TypeInfo::new(), params: result .iter() @@ -3051,6 +3052,13 @@ impl ComponentState { ) -> Result { let mut info = TypeInfo::new(); + if ty.async_ && !self.features.cm_async() { + bail!( + offset, + "async component functions require the component model async feature" + ); + } + let mut set = Set::default(); set.reserve(core::cmp::max( ty.params.len(), @@ -3092,6 +3100,7 @@ impl ComponentState { .transpose()?; Ok(ComponentFuncType { + async_: ty.async_, info, params, result, @@ -4534,11 +4543,8 @@ impl ComponentNameContext { if let ExternKind::Export = kind { match kebab.kind() { ComponentNameKind::Label(_) - | ComponentNameKind::AsyncLabel(_) | ComponentNameKind::Method(_) - | ComponentNameKind::AsyncMethod(_) | ComponentNameKind::Static(_) - | ComponentNameKind::AsyncStatic(_) | ComponentNameKind::Constructor(_) | ComponentNameKind::Interface(_) => {} @@ -4552,7 +4558,7 @@ impl ComponentNameContext { // Validate that the kebab name, if it has structure such as // `[method]a.b`, is indeed valid with respect to known resources. - self.validate(&kebab, ty, types, offset, features) + self.validate(&kebab, ty, types, offset) .with_context(|| format!("{} name `{kebab}` is not valid", kind.desc()))?; // Top-level kebab-names must all be unique, even between both imports @@ -4593,7 +4599,6 @@ impl ComponentNameContext { ty: &ComponentEntityType, types: &TypeAlloc, offset: usize, - features: &WasmFeatures, ) -> Result<()> { let func = || { let id = match ty { @@ -4602,24 +4607,10 @@ impl ComponentNameContext { }; Ok(&types[id]) }; - match name.kind() { - ComponentNameKind::AsyncLabel(_) - | ComponentNameKind::AsyncMethod(_) - | ComponentNameKind::AsyncStatic(_) => { - if !features.cm_async() { - bail!( - offset, - "async kebab-names require the component model async feature" - ); - } - } - _ => {} - } match name.kind() { // No validation necessary for these styles of names ComponentNameKind::Label(_) - | ComponentNameKind::AsyncLabel(_) | ComponentNameKind::Interface(_) | ComponentNameKind::Url(_) | ComponentNameKind::Dependency(_) @@ -4630,6 +4621,9 @@ impl ComponentNameContext { // within this context to match `rname`. ComponentNameKind::Constructor(rname) => { let ty = func()?; + if ty.async_ { + bail!(offset, "constructor function cannot be async"); + } let ty = match ty.result { Some(result) => result, None => bail!(offset, "function should return one value"), @@ -4661,7 +4655,7 @@ impl ComponentNameContext { // Methods must take `(param "self" (borrow $resource))` as the // first argument where `$resources` matches the name `resource` as // named in this context. - ComponentNameKind::Method(name) | ComponentNameKind::AsyncMethod(name) => { + ComponentNameKind::Method(name) => { let ty = func()?; if ty.params.len() == 0 { bail!(offset, "function should have at least one argument"); @@ -4693,7 +4687,7 @@ impl ComponentNameContext { // Static methods don't have much validation beyond that they must // be a function and the resource name referred to must already be // in this context. - ComponentNameKind::Static(name) | ComponentNameKind::AsyncStatic(name) => { + ComponentNameKind::Static(name) => { func()?; if !self.all_resource_names.contains(name.resource().as_str()) { bail!(offset, "static resource name is not known in this context"); diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs index 64ddf1ccae..8db17eb511 100644 --- a/crates/wasmparser/src/validator/component_types.rs +++ b/crates/wasmparser/src/validator/component_types.rs @@ -1086,6 +1086,8 @@ impl TypeData for ComponentInstanceType { pub struct ComponentFuncType { /// Metadata about this function type. pub(crate) info: TypeInfo, + /// Whether or not this is an async function. + pub async_: bool, /// The function parameters. pub params: Box<[(KebabString, ComponentValType)]>, /// The function's result. @@ -3237,6 +3239,15 @@ impl<'a> SubtypeCx<'a> { let a = &self.a[a]; let b = &self.b[b]; + if a.async_ != b.async_ { + let a_desc = if a.async_ { "async" } else { "sync" }; + let b_desc = if b.async_ { "async" } else { "sync" }; + bail!( + offset, + "expected {a_desc} function, found {b_desc} function", + ); + } + // Note that this intentionally diverges from the upstream // specification in terms of subtyping. This is a full // type-equality check which ensures that the structure of `a` diff --git a/crates/wasmparser/src/validator/names.rs b/crates/wasmparser/src/validator/names.rs index a35edea75c..5ec899634e 100644 --- a/crates/wasmparser/src/validator/names.rs +++ b/crates/wasmparser/src/validator/names.rs @@ -242,9 +242,6 @@ impl From for String { /// * a plain method name : `[method]a-b.c-d` /// * a plain static method name : `[static]a-b.c-d` /// * a plain constructor: `[constructor]a-b` -/// * an async plain label: `[async]a-b-c` -/// * an async plain method name : `[async method]a-b.c-d` -/// * an async plain static method name : `[async static]a-b.c-d` /// * an interface name: `wasi:cli/reactor@0.1.0` /// * a dependency name: `locked-dep=foo:bar/baz` /// * a URL name: `url=https://..` @@ -271,9 +268,6 @@ enum ParsedComponentNameKind { Dependency, Url, Hash, - AsyncLabel, - AsyncMethod, - AsyncStatic, } /// Created via [`ComponentName::kind`] and classifies a name. @@ -301,22 +295,11 @@ pub enum ComponentNameKind<'a> { /// `integrity=sha256:...` #[allow(missing_docs)] Hash(HashName<'a>), - /// `[async]a-b-c` - AsyncLabel(&'a KebabStr), - /// `[async method]a-b.c-d` - #[allow(missing_docs)] - AsyncMethod(ResourceFunc<'a>), - /// `[async static]a-b.c-d` - #[allow(missing_docs)] - AsyncStatic(ResourceFunc<'a>), } const CONSTRUCTOR: &str = "[constructor]"; const METHOD: &str = "[method]"; const STATIC: &str = "[static]"; -const ASYNC: &str = "[async]"; -const ASYNC_METHOD: &str = "[async method]"; -const ASYNC_STATIC: &str = "[async static]"; impl ComponentName { /// Attempts to parse `name` as a valid component name, returning `Err` if @@ -352,12 +335,9 @@ impl ComponentName { use ParsedComponentNameKind as PK; match self.kind { PK::Label => Label(KebabStr::new_unchecked(&self.raw)), - PK::AsyncLabel => AsyncLabel(KebabStr::new_unchecked(&self.raw[ASYNC.len()..])), PK::Constructor => Constructor(KebabStr::new_unchecked(&self.raw[CONSTRUCTOR.len()..])), PK::Method => Method(ResourceFunc(&self.raw[METHOD.len()..])), - PK::AsyncMethod => AsyncMethod(ResourceFunc(&self.raw[ASYNC_METHOD.len()..])), PK::Static => Static(ResourceFunc(&self.raw[STATIC.len()..])), - PK::AsyncStatic => AsyncStatic(ResourceFunc(&self.raw[ASYNC_STATIC.len()..])), PK::Interface => Interface(InterfaceName(&self.raw)), PK::Dependency => Dependency(DependencyName(&self.raw)), PK::Url => Url(UrlName(&self.raw)), @@ -427,9 +407,6 @@ impl ComponentNameKind<'_> { Self::Dependency(_) => ParsedComponentNameKind::Dependency, Self::Url(_) => ParsedComponentNameKind::Url, Self::Hash(_) => ParsedComponentNameKind::Hash, - Self::AsyncLabel(_) => ParsedComponentNameKind::AsyncLabel, - Self::AsyncMethod(_) => ParsedComponentNameKind::AsyncMethod, - Self::AsyncStatic(_) => ParsedComponentNameKind::AsyncStatic, } } } @@ -439,22 +416,17 @@ impl Ord for ComponentNameKind<'_> { use ComponentNameKind::*; match (self, other) { - (Label(lhs) | AsyncLabel(lhs), Label(rhs) | AsyncLabel(rhs)) => lhs.cmp(rhs), + (Label(lhs), Label(rhs)) => lhs.cmp(rhs), (Constructor(lhs), Constructor(rhs)) => lhs.cmp(rhs), - ( - Method(lhs) | AsyncMethod(lhs) | Static(lhs) | AsyncStatic(lhs), - Method(rhs) | AsyncMethod(rhs) | Static(rhs) | AsyncStatic(rhs), - ) => lhs.cmp(rhs), - - // `[..]l.l` is equivalent to `l` and `[async]l`. - ( - Label(plain) | AsyncLabel(plain), - Method(method) | AsyncMethod(method) | Static(method) | AsyncStatic(method), - ) - | ( - Method(method) | AsyncMethod(method) | Static(method) | AsyncStatic(method), - Label(plain) | AsyncLabel(plain), - ) if *plain == method.resource() && *plain == method.method() => Ordering::Equal, + (Method(lhs) | Static(lhs), Method(rhs) | Static(rhs)) => lhs.cmp(rhs), + + // `[..]l.l` is equivalent to `l` + (Label(plain), Method(method) | Static(method)) + | (Method(method) | Static(method), Label(plain)) + if *plain == method.resource() && *plain == method.method() => + { + Ordering::Equal + } (Interface(lhs), Interface(rhs)) => lhs.cmp(rhs), (Dependency(lhs), Dependency(rhs)) => lhs.cmp(rhs), @@ -462,12 +434,9 @@ impl Ord for ComponentNameKind<'_> { (Hash(lhs), Hash(rhs)) => lhs.cmp(rhs), (Label(_), _) - | (AsyncLabel(_), _) | (Constructor(_), _) | (Method(_), _) | (Static(_), _) - | (AsyncMethod(_), _) - | (AsyncStatic(_), _) | (Interface(_), _) | (Dependency(_), _) | (Url(_), _) @@ -486,10 +455,10 @@ impl Hash for ComponentNameKind<'_> { fn hash(&self, hasher: &mut H) { use ComponentNameKind::*; match self { - Label(name) | AsyncLabel(name) => (0u8, name).hash(hasher), + Label(name) => (0u8, name).hash(hasher), Constructor(name) => (1u8, name).hash(hasher), - Method(name) | Static(name) | AsyncMethod(name) | AsyncStatic(name) => { + Method(name) | Static(name) => { // `l.l` hashes the same as `l` since they're equal above, // otherwise everything is hashed as `a.b` with a unique // prefix. @@ -630,10 +599,6 @@ struct ComponentNameParser<'a> { impl<'a> ComponentNameParser<'a> { fn parse(&mut self) -> Result { - if self.eat_str(ASYNC) { - self.expect_kebab()?; - return Ok(ParsedComponentNameKind::AsyncLabel); - } if self.eat_str(CONSTRUCTOR) { self.expect_kebab()?; return Ok(ParsedComponentNameKind::Constructor); @@ -650,18 +615,6 @@ impl<'a> ComponentNameParser<'a> { self.expect_kebab()?; return Ok(ParsedComponentNameKind::Static); } - if self.eat_str(ASYNC_METHOD) { - let resource = self.take_until('.')?; - self.kebab(resource)?; - self.expect_kebab()?; - return Ok(ParsedComponentNameKind::AsyncMethod); - } - if self.eat_str(ASYNC_STATIC) { - let resource = self.take_until('.')?; - self.kebab(resource)?; - self.expect_kebab()?; - return Ok(ParsedComponentNameKind::AsyncStatic); - } // 'unlocked-dep=<' '>' if self.eat_str("unlocked-dep=") { diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs index 7b7f001c39..0c15f30fcc 100644 --- a/crates/wasmprinter/src/component.rs +++ b/crates/wasmprinter/src/component.rs @@ -472,6 +472,9 @@ impl Printer<'_, '_> { ty: &ComponentFuncType, ) -> Result<()> { self.start_group("func")?; + if ty.async_ { + self.print_type_keyword(" async")?; + } for (name, ty) in ty.params.iter() { self.result.write_str(" ")?; self.start_group("param ")?; diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index be3759a3ab..fb2e833c0a 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -74,8 +74,8 @@ fn encode_type(encoder: ComponentTypeEncoder, ty: &TypeDef) { } TypeDef::Func(f) => { let mut encoder = encoder.function(); + encoder.async_(f.async_); encoder.params(f.params.iter().map(|p| (p.name, &p.ty))); - encoder.result(f.result.as_ref().map(|ty| ty.into())); } TypeDef::Component(c) => { diff --git a/crates/wast/src/component/types.rs b/crates/wast/src/component/types.rs index 4153f28b54..a6652669d4 100644 --- a/crates/wast/src/component/types.rs +++ b/crates/wast/src/component/types.rs @@ -747,6 +747,8 @@ impl<'a> Parse<'a> for Future<'a> { /// A component function type with parameters and result. #[derive(Debug)] pub struct ComponentFunctionType<'a> { + /// Whether or not this is an `async` fnction. + pub async_: bool, /// The parameters of a function, optionally each having an identifier for /// name resolution and a name for the custom `name` section. pub params: Box<[ComponentFunctionParam<'a>]>, @@ -756,6 +758,7 @@ pub struct ComponentFunctionType<'a> { impl<'a> Parse<'a> for ComponentFunctionType<'a> { fn parse(parser: Parser<'a>) -> Result { + let async_ = parser.parse::>()?.is_some(); let mut params: Vec = Vec::new(); while parser.peek2::()? { params.push(parser.parens(|p| p.parse())?); @@ -771,6 +774,7 @@ impl<'a> Parse<'a> for ComponentFunctionType<'a> { }; Ok(Self { + async_, params: params.into(), result, }) diff --git a/crates/wit-component/src/encoding/types.rs b/crates/wit-component/src/encoding/types.rs index 202febbefa..2dec9fbf5e 100644 --- a/crates/wit-component/src/encoding/types.rs +++ b/crates/wit-component/src/encoding/types.rs @@ -10,6 +10,7 @@ use wit_parser::{ /// Represents a key type for interface function definitions. #[derive(Hash, PartialEq, Eq, Clone)] pub struct FunctionKey<'a> { + async_: bool, params: &'a [(String, Type)], result: &'a Option, } @@ -98,6 +99,7 @@ pub trait ValtypeEncoder<'a> { /// document. fn encode_func_type(&mut self, resolve: &'a Resolve, func: &'a Function) -> Result { let key = FunctionKey { + async_: func.kind.is_async(), params: &func.params, result: &func.result, }; @@ -115,7 +117,7 @@ pub trait ValtypeEncoder<'a> { // Encode the function type let (index, mut f) = self.define_function_type(); - f.params(params).result(result); + f.async_(func.kind.is_async()).params(params).result(result); let prev = self.type_encoding_maps().func_type_map.insert(key, index); assert!(prev.is_none()); Ok(index) diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index c120023f7a..e97e77bfb0 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -487,11 +487,7 @@ impl WitPrinter { self.output.indent_end(); } WorldItem::Function(f) => { - // Note that `f.item_name()` is used here instead of - // `name` because if this is an async function then we - // want to print `foo`, not `[async]foo` under the - // `import` name. - self.print_name_type(f.item_name(), TypeKind::Other); + self.print_name_type(&f.name, TypeKind::Other); self.output.str(": "); self.print_function(resolve, f)?; self.output.semicolon(); diff --git a/crates/wit-component/tests/components/link-lib-with-async-export/component.wat b/crates/wit-component/tests/components/link-lib-with-async-export/component.wat index 2ac086f7a7..cf3c5be089 100644 --- a/crates/wit-component/tests/components/link-lib-with-async-export/component.wat +++ b/crates/wit-component/tests/components/link-lib-with-async-export/component.wat @@ -48,10 +48,10 @@ (type (;1;) (func)) (type (;2;) (func (result i32))) (type (;3;) (func (param i32 i32 i32) (result i32))) - (import "[export]$root" "[task-return][async]f" (func (;0;) (type 0))) + (import "[export]$root" "[task-return]f" (func (;0;) (type 0))) (export "foo" (func 1)) - (export "[async-lift][async]f" (func 2)) - (export "[callback][async-lift][async]f" (func 3)) + (export "[async-lift]f" (func 2)) + (export "[callback][async-lift]f" (func 3)) (func (;1;) (type 1)) (func (;2;) (type 2) (result i32) unreachable @@ -79,9 +79,9 @@ (core module $wit-component-shim-module (;4;) (type (;0;) (func (param i32))) (table (;0;) 1 1 funcref) - (export "0" (func $"task-return-[async]f")) + (export "0" (func $task-return-f)) (export "$imports" (table 0)) - (func $"task-return-[async]f" (;0;) (type 0) (param i32) + (func $task-return-f (;0;) (type 0) (param i32) local.get 0 i32.const 0 call_indirect (type 0) @@ -110,9 +110,9 @@ (with "env" (instance $env)) ) ) - (alias core export $wit-component-shim-instance "0" (core func $"task-return-[async]f" (;1;))) + (alias core export $wit-component-shim-instance "0" (core func $task-return-f (;1;))) (core instance $"[export]$root" (;4;) - (export "[task-return][async]f" (func $"task-return-[async]f")) + (export "[task-return]f" (func $task-return-f)) ) (core instance $foo (;5;) (instantiate $foo (with "[export]$root" (instance $"[export]$root")) @@ -133,11 +133,11 @@ (with "foo" (instance $foo)) ) ) - (type (;4;) (func (result $big))) - (alias core export $foo "[async-lift][async]f" (core func $"[async-lift][async]f" (;3;))) - (alias core export $foo "[callback][async-lift][async]f" (core func $"[callback][async-lift][async]f" (;4;))) - (func $"[async]f" (;0;) (type 4) (canon lift (core func $"[async-lift][async]f") (memory $memory) string-encoding=utf8 async (callback $"[callback][async-lift][async]f"))) - (export $"#func1 [async]f" (@name "[async]f") (;1;) "[async]f" (func $"[async]f")) + (type (;4;) (func async (result $big))) + (alias core export $foo "[async-lift]f" (core func $"[async-lift]f" (;3;))) + (alias core export $foo "[callback][async-lift]f" (core func $"[callback][async-lift]f" (;4;))) + (func $f (;0;) (type 4) (canon lift (core func $"[async-lift]f") (memory $memory) string-encoding=utf8 async (callback $"[callback][async-lift]f"))) + (export $"#func1 f" (@name "f") (;1;) "f" (func $f)) (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/components/link-lib-with-async-export/lib-foo.wat b/crates/wit-component/tests/components/link-lib-with-async-export/lib-foo.wat index e11681efc8..a9b26107a6 100644 --- a/crates/wit-component/tests/components/link-lib-with-async-export/lib-foo.wat +++ b/crates/wit-component/tests/components/link-lib-with-async-export/lib-foo.wat @@ -4,9 +4,9 @@ (needed "c") ) - (import "[export]$root" "[task-return][async]f" (func (param i32))) + (import "[export]$root" "[task-return]f" (func (param i32))) (func (export "foo")) - (func (export "[async-lift][async]f") (result i32) unreachable) - (func (export "[callback][async-lift][async]f") (param i32 i32 i32) (result i32) unreachable) + (func (export "[async-lift]f") (result i32) unreachable) + (func (export "[callback][async-lift]f") (param i32 i32 i32) (result i32) unreachable) ) diff --git a/crates/wit-dylib/Cargo.toml b/crates/wit-dylib/Cargo.toml index c1bb4ff2f6..60479a01f9 100644 --- a/crates/wit-dylib/Cargo.toml +++ b/crates/wit-dylib/Cargo.toml @@ -32,6 +32,7 @@ arbtest = "0.3.2" indexmap = { workspace = true } wit-component = { workspace = true } env_logger = { workspace = true } +wasmparser = { workspace = true } [lib] test = false diff --git a/crates/wit-dylib/test-programs/src/bin/async_callee.rs b/crates/wit-dylib/test-programs/src/bin/async_callee.rs index 9c7040190a..71b11ba09b 100644 --- a/crates/wit-dylib/test-programs/src/bin/async_callee.rs +++ b/crates/wit-dylib/test-programs/src/bin/async_callee.rs @@ -23,7 +23,7 @@ impl TestCase for MyInterpreter { let big = Val::Record(vec![big2.clone(), big2.clone(), big2.clone(), big2.clone()]); match func.name() { - "[async]f" => { + "f" => { assert_eq!(func.params().len(), 0); assert!(func.result().is_none()); assert_eq!(args.len(), 0); @@ -33,7 +33,7 @@ impl TestCase for MyInterpreter { } None } - "[async]f-scalar-param" => { + "f-scalar-param" => { assert_eq!(func.params().len(), 1); assert!(func.result().is_none()); assert_eq!(args.len(), 1); @@ -42,14 +42,14 @@ impl TestCase for MyInterpreter { assert_eq!(args.next(), None); None } - "[async]f-scalar-result" => { + "f-scalar-result" => { assert_eq!(func.params().len(), 0); assert!(func.result().is_some()); assert_eq!(args.len(), 0); Some(Val::U32(202)) } - "[async]aggregates" => { + "aggregates" => { assert_eq!(func.params().len(), 2); assert!(func.result().is_some()); assert_eq!(args.len(), 2); @@ -72,7 +72,7 @@ impl TestCase for MyInterpreter { Val::F32(64.0), ])) } - "[async]indirect-params" => { + "indirect-params" => { assert_eq!(func.params().len(), 2); assert!(func.result().is_none()); assert_eq!(args.len(), 2); @@ -83,7 +83,7 @@ impl TestCase for MyInterpreter { None } - "[async]indirect-params-and-result" => { + "indirect-params-and-result" => { assert_eq!(func.params().len(), 1); assert!(func.result().is_some()); assert_eq!(args.len(), 1); @@ -92,7 +92,7 @@ impl TestCase for MyInterpreter { Some(big.clone()) } - "[async]echo-string" => { + "echo-string" => { assert_eq!(func.params().len(), 1); assert!(func.result().is_some()); args.next() diff --git a/crates/wit-dylib/test-programs/src/bin/async_caller.rs b/crates/wit-dylib/test-programs/src/bin/async_caller.rs index d8c56832e5..12a8f46c41 100644 --- a/crates/wit-dylib/test-programs/src/bin/async_caller.rs +++ b/crates/wit-dylib/test-programs/src/bin/async_caller.rs @@ -18,30 +18,25 @@ impl TestCase for MyInterpreter { args: impl ExactSizeIterator, ) -> Option { assert_eq!(func.interface(), None); - assert_eq!(func.name(), "[async]run"); + assert_eq!(func.name(), "run"); assert_eq!(func.params().len(), 0); assert!(func.result().is_none()); assert_eq!(args.len(), 0); - let ret = Self::call_import_async(wit, Some("a:b/x"), "[async]f", &[]).await; + let ret = Self::call_import_async(wit, Some("a:b/x"), "f", &[]).await; assert!(ret.is_none()); - let ret = Self::call_import_async( - wit, - Some("a:b/x"), - "[async]f-scalar-param", - &[Val::U32(101)], - ) - .await; + let ret = + Self::call_import_async(wit, Some("a:b/x"), "f-scalar-param", &[Val::U32(101)]).await; assert!(ret.is_none()); - let ret = Self::call_import_async(wit, Some("a:b/x"), "[async]f-scalar-result", &[]).await; + let ret = Self::call_import_async(wit, Some("a:b/x"), "f-scalar-result", &[]).await; assert_eq!(ret, Some(Val::U32(202))); let ret = Self::call_import_async( wit, Some("a:b/x"), - "[async]aggregates", + "aggregates", &[ Val::Record(vec![ Val::Record(vec![Val::U32(2000), Val::Char('y')]), @@ -66,7 +61,7 @@ impl TestCase for MyInterpreter { let ret = Self::call_import_async( wit, Some("a:b/x"), - "[async]indirect-params", + "indirect-params", &[big.clone(), big.clone()], ) .await; @@ -75,7 +70,7 @@ impl TestCase for MyInterpreter { let ret = Self::call_import_async( wit, Some("a:b/x"), - "[async]indirect-params-and-result", + "indirect-params-and-result", &[big.clone()], ) .await; @@ -85,7 +80,7 @@ impl TestCase for MyInterpreter { let ret = Self::call_import_async( wit, Some("a:b/x"), - "[async]echo-string", + "echo-string", &[Val::String(s.to_string())], ) .await; diff --git a/crates/wit-dylib/test-programs/src/bin/roundtrip_caller.rs b/crates/wit-dylib/test-programs/src/bin/roundtrip_caller.rs index 5a95da1736..caf4e2f9a2 100644 --- a/crates/wit-dylib/test-programs/src/bin/roundtrip_caller.rs +++ b/crates/wit-dylib/test-programs/src/bin/roundtrip_caller.rs @@ -5,21 +5,20 @@ use test_programs::*; export_test!(struct MyInterpreter); impl TestCase for MyInterpreter { - fn call_export( - _wit: Wit, - _func: ExportFunction, - _args: impl ExactSizeIterator, - ) -> Option { - unreachable!() - } + // // TODO: old signature for when wasmtime re-syncs async support + // async fn call_export_async( + // wit: Wit, + // func: ExportFunction, + // mut args: impl ExactSizeIterator, + // ) -> Option { - async fn call_export_async( + fn call_export( wit: Wit, func: ExportFunction, mut args: impl ExactSizeIterator, ) -> Option { assert_eq!(func.interface(), None); - assert_eq!(func.name(), "[async]run"); + assert_eq!(func.name(), "run"); assert_eq!(func.params().len(), 2); assert!(func.result().is_none()); assert_eq!(args.len(), 2); @@ -93,7 +92,8 @@ impl TestCase for MyInterpreter { }); } let result = if import.is_async() { - Self::call_import_func_async(*import, &args).await + panic!("wasmtime doesn't support async yet"); + // Self::call_import_func_async(*import, &args).await } else { Self::call_import_func(*import, &args) }; diff --git a/crates/wit-dylib/test-programs/src/bin/streams_and_futures_callee.rs b/crates/wit-dylib/test-programs/src/bin/streams_and_futures_callee.rs index 797c9d174f..c183656edc 100644 --- a/crates/wit-dylib/test-programs/src/bin/streams_and_futures_callee.rs +++ b/crates/wit-dylib/test-programs/src/bin/streams_and_futures_callee.rs @@ -62,7 +62,7 @@ impl TestCase for MyInterpreter { assert_eq!(func.interface(), Some("a:b/x")); match func.name() { - "[async]echo-stream-u8" => { + "echo-stream-u8" => { assert_eq!(func.params().len(), 1); assert!(matches!(func.params().next(), Some(Type::Stream(_)))); assert!(matches!(func.result(), Some(Type::Stream(_)))); @@ -101,7 +101,7 @@ impl TestCase for MyInterpreter { Some(Val::Stream(result.take_handle())) } - "[async]echo-future-string" => { + "echo-future-string" => { assert_eq!(func.params().len(), 1); assert!(matches!(func.params().next(), Some(Type::Future(_)))); assert!(matches!(func.result(), Some(Type::Future(_)))); @@ -126,7 +126,7 @@ impl TestCase for MyInterpreter { Some(Val::Future(result.take_handle())) } - "[async method]thing.get" => { + "thing.get" => { assert_eq!(func.params().len(), 1); assert!(matches!(func.params().next(), Some(Type::Borrow(_)))); assert!(matches!(func.result(), Some(Type::String))); @@ -147,7 +147,7 @@ impl TestCase for MyInterpreter { Some(Val::String(value)) } - "[async]short-reads" => { + "short-reads" => { assert_eq!(func.params().len(), 1); assert!(matches!(func.params().next(), Some(Type::Stream(_)))); assert!(matches!(func.result(), Some(Type::Stream(_)))); diff --git a/crates/wit-dylib/test-programs/src/bin/streams_and_futures_caller.rs b/crates/wit-dylib/test-programs/src/bin/streams_and_futures_caller.rs index bedb74a8e1..2520c7b439 100644 --- a/crates/wit-dylib/test-programs/src/bin/streams_and_futures_caller.rs +++ b/crates/wit-dylib/test-programs/src/bin/streams_and_futures_caller.rs @@ -24,14 +24,14 @@ impl TestCase for MyInterpreter { args: impl ExactSizeIterator, ) -> Option { assert_eq!(func.interface(), None); - assert_eq!(func.name(), "[async]run"); + assert_eq!(func.name(), "run"); assert_eq!(func.params().len(), 0); assert!(func.result().is_none()); assert_eq!(args.len(), 0); { let Some(Type::Stream(ty)) = wit - .unwrap_import(Some("a:b/x"), "[async]echo-stream-u8") + .unwrap_import(Some("a:b/x"), "echo-stream-u8") .params() .next() else { @@ -45,7 +45,7 @@ impl TestCase for MyInterpreter { let Some(Val::Stream(rx)) = Self::call_import_async( wit, Some("a:b/x"), - "[async]echo-stream-u8", + "echo-stream-u8", &[Val::Stream(rx.take_handle())], ) .await @@ -65,7 +65,7 @@ impl TestCase for MyInterpreter { { let Some(Type::Future(ty)) = wit - .unwrap_import(Some("a:b/x"), "[async]echo-future-string") + .unwrap_import(Some("a:b/x"), "echo-future-string") .params() .next() else { @@ -79,7 +79,7 @@ impl TestCase for MyInterpreter { let Some(Val::Future(rx)) = Self::call_import_async( wit, Some("a:b/x"), - "[async]echo-future-string", + "echo-future-string", &[Val::Future(rx.take_handle())], ) .await @@ -117,7 +117,7 @@ impl TestCase for MyInterpreter { .collect::>(); let Some(Type::Stream(ty)) = wit - .unwrap_import(Some("a:b/x"), "[async]short-reads") + .unwrap_import(Some("a:b/x"), "short-reads") .params() .next() else { @@ -131,7 +131,7 @@ impl TestCase for MyInterpreter { let Some(Val::Stream(rx)) = Self::call_import_async( wit, Some("a:b/x"), - "[async]short-reads", + "short-reads", &[Val::Stream(rx.take_handle())], ) .await @@ -168,7 +168,7 @@ impl TestCase for MyInterpreter { let Val::String(value) = Self::call_import_async( wit, Some("a:b/x"), - "[async method]thing.get", + "thing.get", &[Val::Borrow(handle.borrow())], ) .await diff --git a/crates/wit-dylib/tests/all.rs b/crates/wit-dylib/tests/all.rs index 97cd69b25e..f5e3a342bf 100644 --- a/crates/wit-dylib/tests/all.rs +++ b/crates/wit-dylib/tests/all.rs @@ -3,6 +3,7 @@ use libtest_mimic::{Arguments, Trial}; use std::path::Path; use std::process::Command; use tempfile::TempDir; +use wasmparser::{Validator, WasmFeatures}; use wit_parser::Resolve; fn main() { @@ -78,5 +79,26 @@ fn run_test(tempdir: &TempDir, caller: &Path, callee: &Path, wit: &Path) -> Resu )); } + if uses_async_and_wasmtime_does_not_support_async(&composition_file, &error)? { + return Ok(()); + } + anyhow::bail!("{error}") } + +fn uses_async_and_wasmtime_does_not_support_async(wasm: &Path, error: &str) -> Result { + if !error.contains("invalid leading byte (0x43) for component defined type") { + return Ok(false); + } + let wasm = std::fs::read(wasm)?; + + let validates_with_cm_async = Validator::new_with_features(WasmFeatures::all()) + .validate_all(&wasm) + .is_ok(); + let validates_without_cm_async = + Validator::new_with_features(WasmFeatures::all() ^ WasmFeatures::CM_ASYNC) + .validate_all(&wasm) + .is_ok(); + + Ok(validates_with_cm_async && !validates_without_cm_async) +} diff --git a/crates/wit-dylib/tests/roundtrip.rs b/crates/wit-dylib/tests/roundtrip.rs index 5f43bc6d31..3cebbd2706 100644 --- a/crates/wit-dylib/tests/roundtrip.rs +++ b/crates/wit-dylib/tests/roundtrip.rs @@ -65,7 +65,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> { config.fixed_size_list = false; config.futures = false; // TODO config.streams = false; // TODO - config.async_ = true; + config.async_ = false; let wasm = wit_smith::smith(&config, u)?; std::fs::write("./hello.wasm", &wasm).unwrap(); let (mut resolve, _pkg) = match wit_parser::decoding::decode(&wasm).unwrap() { @@ -203,10 +203,10 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> { // Inject the actual entrypoint of the test. resolve.worlds[caller].exports.insert( - WorldKey::Name("[async]run".to_string()), + WorldKey::Name("run".to_string()), WorldItem::Function(Function { - name: "[async]run".to_string(), - kind: FunctionKind::AsyncFreestanding, + name: "run".to_string(), + kind: FunctionKind::Freestanding, params: vec![ ("iters".to_string(), Type::U32), ("seed".to_string(), Type::U64), diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 761668e44f..a997add506 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -776,12 +776,10 @@ impl<'a> Resolver<'a> { Ok(WorldItem::Interface { id, stability }) } ast::ExternKind::Func(name, func) => { - let prefix = if func.async_ { "[async]" } else { "" }; - let name = format!("{prefix}{}", name.name); let func = self.resolve_function( docs, attrs, - &name, + &name.name, func, if func.async_ { FunctionKind::AsyncFreestanding @@ -833,12 +831,10 @@ impl<'a> Resolver<'a> { match field { ast::InterfaceItem::Func(f) => { self.define_interface_name(&f.name, TypeOrItem::Item("function"))?; - let prefix = if f.func.async_ { "[async]" } else { "" }; - let name = format!("{prefix}{}", f.name.name); funcs.push(self.resolve_function( &f.docs, &f.attributes, - &name, + &f.name.name, &f.func, if f.func.async_ { FunctionKind::AsyncFreestanding @@ -1039,8 +1035,7 @@ impl<'a> Resolver<'a> { let async_ = named_func.func.async_; match func { ast::ResourceFunc::Method(f) => { - let prefix = if async_ { "[async method]" } else { "[method]" }; - name = format!("{prefix}{}.{}", resource.name, f.name.name); + name = format!("[method]{}.{}", resource.name, f.name.name); kind = if async_ { FunctionKind::AsyncMethod(resource_id) } else { @@ -1048,8 +1043,7 @@ impl<'a> Resolver<'a> { }; } ast::ResourceFunc::Static(f) => { - let prefix = if async_ { "[async static]" } else { "[static]" }; - name = format!("{prefix}{}.{}", resource.name, f.name.name); + name = format!("[static]{}.{}", resource.name, f.name.name); kind = if async_ { FunctionKind::AsyncStatic(resource_id) } else { diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 6c58ee1e43..54b023a9a3 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -1198,22 +1198,29 @@ impl WitPackageDecoder<'_> { docs: Default::default(), stability: Default::default(), kind: match name.kind() { - ComponentNameKind::Label(_) => FunctionKind::Freestanding, - ComponentNameKind::AsyncLabel(_) => FunctionKind::AsyncFreestanding, + ComponentNameKind::Label(_) => { + if ty.async_ { + FunctionKind::AsyncFreestanding + } else { + FunctionKind::Freestanding + } + } ComponentNameKind::Constructor(resource) => { FunctionKind::Constructor(self.resources[&owner][resource.as_str()]) } ComponentNameKind::Method(name) => { - FunctionKind::Method(self.resources[&owner][name.resource().as_str()]) - } - ComponentNameKind::AsyncMethod(name) => { - FunctionKind::AsyncMethod(self.resources[&owner][name.resource().as_str()]) + if ty.async_ { + FunctionKind::AsyncMethod(self.resources[&owner][name.resource().as_str()]) + } else { + FunctionKind::Method(self.resources[&owner][name.resource().as_str()]) + } } ComponentNameKind::Static(name) => { - FunctionKind::Static(self.resources[&owner][name.resource().as_str()]) - } - ComponentNameKind::AsyncStatic(name) => { - FunctionKind::AsyncStatic(self.resources[&owner][name.resource().as_str()]) + if ty.async_ { + FunctionKind::AsyncStatic(self.resources[&owner][name.resource().as_str()]) + } else { + FunctionKind::Static(self.resources[&owner][name.resource().as_str()]) + } } // Functions shouldn't have ID-based names at this time. diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 7274758491..e591e775d1 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -450,39 +450,12 @@ pub enum WorldKey { Interface(InterfaceId), } -/// Helper type to deduplicate kebab strings in maps. -/// -/// The component model doesn't allow importing both `x` and `[async]x` and this -/// type is what helps encapsulate that rule. This is similar ish to `KebabStr` -/// inside of `wasmparser` except that it's only used here for hashing/equality -/// of `WorldKey` to ensure that world items all hash/dedupe as expected. -#[derive(PartialEq, Hash)] -enum KebabDedupe<'a> { - Normal(&'a str), -} - -impl<'a> From<&'a str> for KebabDedupe<'a> { - fn from(s: &'a str) -> KebabDedupe<'a> { - // If the name starts with `[async]` then strip it off. That conflicts - // with names without `[async]` so pretend it's the same. - if let Some(s) = s.strip_prefix("[async]") { - return KebabDedupe::Normal(s); - } - - // Note that at this time there's no handling of `[method]` or - // `[static]` or similar. In theory that might work here but that's - // already handled via other means in `wit-parser` at this time and - // `[async]` is chosen to be handled here. - KebabDedupe::Normal(s) - } -} - impl Hash for WorldKey { fn hash(&self, hasher: &mut H) { match self { WorldKey::Name(s) => { 0u8.hash(hasher); - KebabDedupe::from(s.as_str()).hash(hasher); + s.as_str().hash(hasher); } WorldKey::Interface(i) => { 1u8.hash(hasher); @@ -495,9 +468,7 @@ impl Hash for WorldKey { impl PartialEq for WorldKey { fn eq(&self, other: &WorldKey) -> bool { match (self, other) { - (WorldKey::Name(a), WorldKey::Name(b)) => { - KebabDedupe::from(a.as_str()) == KebabDedupe::from(b.as_str()) - } + (WorldKey::Name(a), WorldKey::Name(b)) => a.as_str() == b.as_str(), (WorldKey::Name(_), _) => false, (WorldKey::Interface(a), WorldKey::Interface(b)) => a == b, (WorldKey::Interface(_), _) => false, @@ -957,6 +928,19 @@ pub enum FunctionKind { } impl FunctionKind { + /// Returns whether this is an async function or not. + pub fn is_async(&self) -> bool { + match self { + FunctionKind::Freestanding + | FunctionKind::Method(_) + | FunctionKind::Static(_) + | FunctionKind::Constructor(_) => false, + FunctionKind::AsyncFreestanding + | FunctionKind::AsyncMethod(_) + | FunctionKind::AsyncStatic(_) => true, + } + } + /// Returns the resource, if present, that this function kind refers to. pub fn resource(&self) -> Option { match self { @@ -1121,8 +1105,7 @@ impl ManglingAndAbi { impl Function { pub fn item_name(&self) -> &str { match &self.kind { - FunctionKind::Freestanding => &self.name, - FunctionKind::AsyncFreestanding => &self.name["[async]".len()..], + FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &self.name, FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::AsyncMethod(_) diff --git a/crates/wit-parser/tests/ui/async.wit.json b/crates/wit-parser/tests/ui/async.wit.json index b2510cbfb3..fd009db69b 100644 --- a/crates/wit-parser/tests/ui/async.wit.json +++ b/crates/wit-parser/tests/ui/async.wit.json @@ -3,9 +3,9 @@ { "name": "y", "imports": { - "[async]x": { + "x": { "function": { - "name": "[async]x", + "name": "x", "kind": "async-freestanding", "params": [] } @@ -19,9 +19,9 @@ } }, "exports": { - "[async]x": { + "x": { "function": { - "name": "[async]x", + "name": "x", "kind": "async-freestanding", "params": [] } @@ -44,8 +44,8 @@ "z": 0 }, "functions": { - "[async]x": { - "name": "[async]x", + "x": { + "name": "x", "kind": "async-freestanding", "params": [ { @@ -68,8 +68,8 @@ "params": [], "result": 2 }, - "[async method]z.x": { - "name": "[async method]z.x", + "[method]z.x": { + "name": "[method]z.x", "kind": { "async-method": 0 }, @@ -92,8 +92,8 @@ } ] }, - "[async static]z.static-x": { - "name": "[async static]z.static-x", + "[static]z.static-x": { + "name": "[static]z.static-x", "kind": { "async-static": 0 }, diff --git a/crates/wit-parser/tests/ui/parse-fail/async-bad-world3.wit.result b/crates/wit-parser/tests/ui/parse-fail/async-bad-world3.wit.result index 706856243d..42627ce954 100644 --- a/crates/wit-parser/tests/ui/parse-fail/async-bad-world3.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/async-bad-world3.wit.result @@ -1,4 +1,4 @@ -import `[async]x` conflicts with prior type of same name +import `x` conflicts with prior type of same name --> tests/ui/parse-fail/async-bad-world3.wit:4:10 | 4 | import x: async func(); diff --git a/crates/wit-parser/tests/ui/parse-fail/async-bad-world4.wit.result b/crates/wit-parser/tests/ui/parse-fail/async-bad-world4.wit.result index 771b8619be..07282a4c1c 100644 --- a/crates/wit-parser/tests/ui/parse-fail/async-bad-world4.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/async-bad-world4.wit.result @@ -1,4 +1,4 @@ -import of `[async]x` shadows previously imported items +import of `x` shadows previously imported items --> tests/ui/parse-fail/async-bad-world4.wit:8:11 | 8 | include a; diff --git a/tests/cli/component-model/async/names.wast b/tests/cli/component-model/async/names.wast index ee43c9b3c0..5208920568 100644 --- a/tests/cli/component-model/async/names.wast +++ b/tests/cli/component-model/async/names.wast @@ -1,88 +1,29 @@ ;; RUN: wast --assert default --snapshot tests/snapshots % -f cm-async -(component - (import "[async]f" (func)) - (import "r" (type $r (sub resource))) - (import "[async method]r.f" (func (param "self" (borrow $r)))) - (import "[async static]r.f2" (func)) -) - -;; name conflicts where the "base kebab name" only differs on abi/etc and these -;; should all conflict with one another. +;; historically these were part of the component-model-async development but +;; they have since been removed. (assert_invalid (component (import "[async]f" (func)) - (import "f" (func)) ) - "conflicts with previous name") + "not in kebab case") (assert_invalid (component - (import "f" (func)) - (import "[async]f" (func)) + (import "[async method]f" (func)) ) - "conflicts with previous name") - -(assert_invalid - (component - (import "r" (type $r (sub resource))) - (import "[method]r.f" (func (param "self" (borrow $r)))) - (import "[async static]r.f" (func))) - "conflicts with previous name") - -(assert_invalid - (component - (import "r" (type $r (sub resource))) - (import "[method]r.f" (func (param "self" (borrow $r)))) - (import "[async method]r.f" (func (param "self" (borrow $r))))) - "conflicts with previous name") - -(assert_invalid - (component - (import "r" (type $r (sub resource))) - (import "[method]r.f" (func (param "self" (borrow $r)))) - (import "[async static]r.f" (func))) - "conflicts with previous name") - -(assert_invalid - (component - (import "r" (type $r (sub resource))) - (import "[async method]r.f" (func (param "self" (borrow $r)))) - (import "[async method]r.f" (func (param "self" (borrow $r))))) - "conflicts with previous name") - -(assert_invalid - (component - (import "r" (type $r (sub resource))) - (import "[async method]r.f" (func (param "self" (borrow $r)))) - (import "[static]r.f" (func))) - "conflicts with previous name") - -(assert_invalid - (component - (import "r" (type $r (sub resource))) - (import "[async method]r.f" (func (param "self" (borrow $r)))) - (import "[async static]r.f" (func))) - "conflicts with previous name") + "not in kebab case") (assert_invalid (component - (import "r" (type $r (sub resource))) - (import "[async static]r.f" (func)) - (import "[static]r.f" (func))) - "conflicts with previous name") - -(assert_invalid - (component - (import "a" (type $a (sub resource))) - (import "[async method]a.a" (func (param "self" (borrow $a)))) + (import "[async static]f" (func)) ) - "import name `[async method]a.a` conflicts with previous name `a`") + "not in kebab case") + (assert_invalid (component (import "a" (type $a (sub resource))) - (import "[async static]a.a" (func)) - ) - "import name `[async static]a.a` conflicts with previous name `a`") + (import "[constructor]a" (func async (result (own $a))))) + "constructor function cannot be async") diff --git a/tests/cli/dummy-ctor-static-async-confusion.wit b/tests/cli/dummy-ctor-static-async-confusion.wit new file mode 100644 index 0000000000..77238a6d0e --- /dev/null +++ b/tests/cli/dummy-ctor-static-async-confusion.wit @@ -0,0 +1,16 @@ +// RUN: component embed --dummy-names legacy --async-stackful % -t | \ +// component new | \ +// validate -f cm-async + +package a:b; + +interface i { + resource x { + foo: static async func() -> x; + constructor(); + } +} + +world w { + import i; +} diff --git a/tests/cli/dump/alias.wat.stdout b/tests/cli/dump/alias.wat.stdout index 7100013041..8bc1eb18dc 100644 --- a/tests/cli/dump/alias.wat.stdout +++ b/tests/cli/dump/alias.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 1f | component type section 0xa | 01 | 1 count - 0xb | 42 04 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { params: [], result: None })), Export { name: ComponentExportName("f1"), ty: Func(0) }, Type(Func(ComponentFuncType { params: [("p1", Primitive(String))], result: None })), Export { name: ComponentExportName("f2"), ty: Func(1) }]) + 0xb | 42 04 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Export { name: ComponentExportName("f1"), ty: Func(0) }, Type(Func(ComponentFuncType { async_: false, params: [("p1", Primitive(String))], result: None })), Export { name: ComponentExportName("f2"), ty: Func(1) }]) | 00 01 00 04 | 00 02 66 31 | 01 00 01 40 diff --git a/tests/cli/dump/alias2.wat.stdout b/tests/cli/dump/alias2.wat.stdout index 47695fca9e..07f1aed469 100644 --- a/tests/cli/dump/alias2.wat.stdout +++ b/tests/cli/dump/alias2.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 30 | component type section 0xa | 01 | 1 count - 0xb | 42 09 00 50 | [type 0] Instance([CoreType(Module([])), Export { name: ComponentExportName("a"), ty: Module(0) }, Type(Func(ComponentFuncType { params: [], result: None })), Export { name: ComponentExportName("b"), ty: Func(0) }, Export { name: ComponentExportName("c"), ty: Value(Primitive(String)) }, Type(Instance([])), Export { name: ComponentExportName("d"), ty: Instance(1) }, Type(Component([])), Export { name: ComponentExportName("e"), ty: Component(2) }]) + 0xb | 42 09 00 50 | [type 0] Instance([CoreType(Module([])), Export { name: ComponentExportName("a"), ty: Module(0) }, Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Export { name: ComponentExportName("b"), ty: Func(0) }, Export { name: ComponentExportName("c"), ty: Value(Primitive(String)) }, Type(Instance([])), Export { name: ComponentExportName("d"), ty: Instance(1) }, Type(Component([])), Export { name: ComponentExportName("e"), ty: Component(2) }]) | 00 04 00 01 | 61 00 11 00 | 01 40 00 01 @@ -30,7 +30,7 @@ | 11 00 0x5a | 07 05 | component type section 0x5c | 01 | 1 count - 0x5d | 40 00 01 00 | [type 0] Func(ComponentFuncType { params: [], result: None }) + 0x5d | 40 00 01 00 | [type 0] Func(ComponentFuncType { async_: false, params: [], result: None }) 0x61 | 0a 0b | component import section 0x63 | 02 | 2 count 0x64 | 00 01 62 01 | [func 0] ComponentImport { name: ComponentImportName("b"), ty: Func(0) } diff --git a/tests/cli/dump/bundled.wat.stdout b/tests/cli/dump/bundled.wat.stdout index ff40ee87fe..a7c135a07d 100644 --- a/tests/cli/dump/bundled.wat.stdout +++ b/tests/cli/dump/bundled.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 30 | component type section 0xa | 01 | 1 count - 0xb | 42 06 01 70 | [type 0] Instance([Type(Defined(List(Primitive(U8)))), Type(Func(ComponentFuncType { params: [("len", Primitive(U32))], result: Some(Type(0)) })), Export { name: ComponentExportName("read"), ty: Func(1) }, Type(Defined(List(Primitive(U8)))), Type(Func(ComponentFuncType { params: [("buf", Type(2))], result: Some(Primitive(U32)) })), Export { name: ComponentExportName("write"), ty: Func(3) }]) + 0xb | 42 06 01 70 | [type 0] Instance([Type(Defined(List(Primitive(U8)))), Type(Func(ComponentFuncType { async_: false, params: [("len", Primitive(U32))], result: Some(Type(0)) })), Export { name: ComponentExportName("read"), ty: Func(1) }, Type(Defined(List(Primitive(U8)))), Type(Func(ComponentFuncType { async_: false, params: [("buf", Type(2))], result: Some(Primitive(U32)) })), Export { name: ComponentExportName("write"), ty: Func(3) }]) | 7d 01 40 01 | 03 6c 65 6e | 79 00 00 04 @@ -176,7 +176,7 @@ | 65 12 02 0x1ad | 07 05 | component type section 0x1af | 01 | 1 count - 0x1b0 | 40 00 01 00 | [type 1] Func(ComponentFuncType { params: [], result: None }) + 0x1b0 | 40 00 01 00 | [type 1] Func(ComponentFuncType { async_: false, params: [], result: None }) 0x1b4 | 06 1e | component alias section 0x1b6 | 03 | 3 count 0x1b7 | 00 00 01 03 | alias [core func 2] CoreInstanceExport { kind: Func, instance_index: 3, name: "play" } diff --git a/tests/cli/dump/component-inline-type.wat.stdout b/tests/cli/dump/component-inline-type.wat.stdout index 8023a9b01b..4d0d178807 100644 --- a/tests/cli/dump/component-inline-type.wat.stdout +++ b/tests/cli/dump/component-inline-type.wat.stdout @@ -23,7 +23,7 @@ | 01 0x30 | 07 05 | component type section 0x32 | 01 | 1 count - 0x33 | 40 00 01 00 | [type 2] Func(ComponentFuncType { params: [], result: None }) + 0x33 | 40 00 01 00 | [type 2] Func(ComponentFuncType { async_: false, params: [], result: None }) 0x37 | 0a 06 | component import section 0x39 | 01 | 1 count 0x3a | 00 01 64 01 | [func 0] ComponentImport { name: ComponentImportName("d"), ty: Func(2) } diff --git a/tests/cli/dump/component-instance-type.wat.stdout b/tests/cli/dump/component-instance-type.wat.stdout index 52b5bdc484..8b98d2b96e 100644 --- a/tests/cli/dump/component-instance-type.wat.stdout +++ b/tests/cli/dump/component-instance-type.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 28 | component type section 0xa | 01 | 1 count - 0xb | 42 03 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { params: [], result: None })), Type(Component([Type(Func(ComponentFuncType { params: [], result: None })), Alias(Outer { kind: Type, count: 1, index: 0 }), Import(ComponentImport { name: ComponentImportName("1"), ty: Func(0) }), Export { name: ComponentExportName("1"), ty: Func(1) }])), Export { name: ComponentExportName("c5"), ty: Component(1) }]) + 0xb | 42 03 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Type(Component([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Alias(Outer { kind: Type, count: 1, index: 0 }), Import(ComponentImport { name: ComponentImportName("1"), ty: Func(0) }), Export { name: ComponentExportName("1"), ty: Func(1) }])), Export { name: ComponentExportName("c5"), ty: Component(1) }]) | 00 01 00 01 | 41 04 01 40 | 00 01 00 02 diff --git a/tests/cli/dump/component-linking.wat.stdout b/tests/cli/dump/component-linking.wat.stdout index c69dec6467..1e5e5cc3d9 100644 --- a/tests/cli/dump/component-linking.wat.stdout +++ b/tests/cli/dump/component-linking.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 30 | component type section 0xa | 01 | 1 count - 0xb | 42 09 00 50 | [type 0] Instance([CoreType(Module([])), Export { name: ComponentExportName("a"), ty: Module(0) }, Type(Func(ComponentFuncType { params: [], result: None })), Export { name: ComponentExportName("b"), ty: Func(0) }, Export { name: ComponentExportName("c"), ty: Value(Primitive(String)) }, Type(Instance([])), Export { name: ComponentExportName("d"), ty: Instance(1) }, Type(Component([])), Export { name: ComponentExportName("e"), ty: Component(2) }]) + 0xb | 42 09 00 50 | [type 0] Instance([CoreType(Module([])), Export { name: ComponentExportName("a"), ty: Module(0) }, Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Export { name: ComponentExportName("b"), ty: Func(0) }, Export { name: ComponentExportName("c"), ty: Value(Primitive(String)) }, Type(Instance([])), Export { name: ComponentExportName("d"), ty: Instance(1) }, Type(Component([])), Export { name: ComponentExportName("e"), ty: Component(2) }]) | 00 04 00 01 | 61 00 11 00 | 01 40 00 01 @@ -30,7 +30,7 @@ | 11 00 0x5a | 07 05 | component type section 0x5c | 01 | 1 count - 0x5d | 40 00 01 00 | [type 0] Func(ComponentFuncType { params: [], result: None }) + 0x5d | 40 00 01 00 | [type 0] Func(ComponentFuncType { async_: false, params: [], result: None }) 0x61 | 0a 0b | component import section 0x63 | 02 | 2 count 0x64 | 00 01 62 01 | [func 0] ComponentImport { name: ComponentImportName("b"), ty: Func(0) } diff --git a/tests/cli/dump/component-outer-alias.wat.stdout b/tests/cli/dump/component-outer-alias.wat.stdout index 7ce95d09ec..8fe249003f 100644 --- a/tests/cli/dump/component-outer-alias.wat.stdout +++ b/tests/cli/dump/component-outer-alias.wat.stdout @@ -3,7 +3,7 @@ 0x8 | 07 0e | component type section 0xa | 02 | 2 count 0xb | 73 | [type 0] Defined(Primitive(String)) - 0xc | 41 02 02 03 | [type 1] Component([Alias(Outer { kind: Type, count: 1, index: 0 }), Type(Func(ComponentFuncType { params: [], result: Some(Type(0)) }))]) + 0xc | 41 02 02 03 | [type 1] Component([Alias(Outer { kind: Type, count: 1, index: 0 }), Type(Func(ComponentFuncType { async_: false, params: [], result: Some(Type(0)) }))]) | 02 01 00 01 | 40 00 00 00 0x18 | 04 2e | [component 0] inline size @@ -14,7 +14,7 @@ 0x25 | 03 02 01 00 | alias [type 0] Outer { kind: Type, count: 1, index: 0 } 0x29 | 07 05 | component type section 0x2b | 01 | 1 count - 0x2c | 40 00 00 00 | [type 1] Func(ComponentFuncType { params: [], result: Some(Type(0)) }) + 0x2c | 40 00 00 00 | [type 1] Func(ComponentFuncType { async_: false, params: [], result: Some(Type(0)) }) 0x30 | 00 16 | custom section 0x32 | 0e 63 6f 6d | name: "component-name" | 70 6f 6e 65 @@ -31,8 +31,8 @@ 0x55 | 03 02 01 00 | alias [type 0] Outer { kind: Type, count: 1, index: 0 } 0x59 | 07 09 | component type section 0x5b | 02 | 2 count - 0x5c | 40 00 00 00 | [type 1] Func(ComponentFuncType { params: [], result: Some(Type(0)) }) - 0x60 | 40 00 00 00 | [type 2] Func(ComponentFuncType { params: [], result: Some(Type(0)) }) + 0x5c | 40 00 00 00 | [type 1] Func(ComponentFuncType { async_: false, params: [], result: Some(Type(0)) }) + 0x60 | 40 00 00 00 | [type 2] Func(ComponentFuncType { async_: false, params: [], result: Some(Type(0)) }) 0x64 | 00 16 | custom section 0x66 | 0e 63 6f 6d | name: "component-name" | 70 6f 6e 65 @@ -52,8 +52,8 @@ 0x8d | 03 02 01 02 | alias [type 0] Outer { kind: Type, count: 1, index: 2 } 0x91 | 07 09 | component type section 0x93 | 02 | 2 count - 0x94 | 40 00 00 00 | [type 1] Func(ComponentFuncType { params: [], result: Some(Type(0)) }) - 0x98 | 40 00 00 00 | [type 2] Func(ComponentFuncType { params: [], result: Some(Type(0)) }) + 0x94 | 40 00 00 00 | [type 1] Func(ComponentFuncType { async_: false, params: [], result: Some(Type(0)) }) + 0x98 | 40 00 00 00 | [type 2] Func(ComponentFuncType { async_: false, params: [], result: Some(Type(0)) }) 0x9c | 00 17 | custom section 0x9e | 0e 63 6f 6d | name: "component-name" | 70 6f 6e 65 diff --git a/tests/cli/dump/instance-expand.wat.stdout b/tests/cli/dump/instance-expand.wat.stdout index d90ee35d82..48cf6e3d5c 100644 --- a/tests/cli/dump/instance-expand.wat.stdout +++ b/tests/cli/dump/instance-expand.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 0d | component type section 0xa | 01 | 1 count - 0xb | 42 02 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { params: [], result: None })), Export { name: ComponentExportName(""), ty: Func(0) }]) + 0xb | 42 02 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Export { name: ComponentExportName(""), ty: Func(0) }]) | 00 01 00 04 | 00 00 01 00 0x17 | 0a 06 | component import section diff --git a/tests/cli/dump/instance-type.wat.stdout b/tests/cli/dump/instance-type.wat.stdout index 0fecf8a819..9d1e20356a 100644 --- a/tests/cli/dump/instance-type.wat.stdout +++ b/tests/cli/dump/instance-type.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 17 | component type section 0xa | 02 | 2 count - 0xb | 42 02 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { params: [], result: None })), Export { name: ComponentExportName(""), ty: Func(0) }]) + 0xb | 42 02 01 40 | [type 0] Instance([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Export { name: ComponentExportName(""), ty: Func(0) }]) | 00 01 00 04 | 00 00 01 00 0x17 | 42 02 01 42 | [type 1] Instance([Type(Instance([])), Export { name: ComponentExportName(""), ty: Instance(0) }]) diff --git a/tests/cli/dump/instantiate.wat.stdout b/tests/cli/dump/instantiate.wat.stdout index edd401fb4e..84883c42aa 100644 --- a/tests/cli/dump/instantiate.wat.stdout +++ b/tests/cli/dump/instantiate.wat.stdout @@ -9,7 +9,7 @@ | 00 0x15 | 07 05 | component type section 0x17 | 01 | 1 count - 0x18 | 40 00 01 00 | [type 1] Func(ComponentFuncType { params: [], result: None }) + 0x18 | 40 00 01 00 | [type 1] Func(ComponentFuncType { async_: false, params: [], result: None }) 0x1c | 0a 06 | component import section 0x1e | 01 | 1 count 0x1f | 00 01 66 01 | [func 0] ComponentImport { name: ComponentImportName("f"), ty: Func(1) } diff --git a/tests/cli/dump/instantiate2.wat.stdout b/tests/cli/dump/instantiate2.wat.stdout index bf505f389f..194a87c901 100644 --- a/tests/cli/dump/instantiate2.wat.stdout +++ b/tests/cli/dump/instantiate2.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 07 0e | component type section 0xa | 01 | 1 count - 0xb | 41 02 01 40 | [type 0] Component([Type(Func(ComponentFuncType { params: [], result: None })), Import(ComponentImport { name: ComponentImportName("a"), ty: Func(0) })]) + 0xb | 41 02 01 40 | [type 0] Component([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Import(ComponentImport { name: ComponentImportName("a"), ty: Func(0) })]) | 00 01 00 03 | 00 01 61 01 | 00 diff --git a/tests/cli/dump/nested-component.wat.stdout b/tests/cli/dump/nested-component.wat.stdout index 3a412280d8..5c44753924 100644 --- a/tests/cli/dump/nested-component.wat.stdout +++ b/tests/cli/dump/nested-component.wat.stdout @@ -35,7 +35,7 @@ 0x64 | 01 6d | "m" 0x66 | 07 08 | component type section 0x68 | 01 | 1 count - 0x69 | 40 01 01 70 | [type 0] Func(ComponentFuncType { params: [("p", Primitive(String))], result: None }) + 0x69 | 40 01 01 70 | [type 0] Func(ComponentFuncType { async_: false, params: [("p", Primitive(String))], result: None }) | 73 01 00 0x70 | 0a 06 | component import section 0x72 | 01 | 1 count @@ -47,7 +47,7 @@ | 11 00 00 0x82 | 07 0e | component type section 0x84 | 01 | 1 count - 0x85 | 42 02 01 40 | [type 1] Instance([Type(Func(ComponentFuncType { params: [], result: None })), Export { name: ComponentExportName("a"), ty: Func(0) }]) + 0x85 | 42 02 01 40 | [type 1] Instance([Type(Func(ComponentFuncType { async_: false, params: [], result: None })), Export { name: ComponentExportName("a"), ty: Func(0) }]) | 00 01 00 04 | 00 01 61 01 | 00 diff --git a/tests/cli/missing-features/component-model/async.wast b/tests/cli/missing-features/component-model/async.wast index 5b8e4a14ee..3682381e72 100644 --- a/tests/cli/missing-features/component-model/async.wast +++ b/tests/cli/missing-features/component-model/async.wast @@ -367,17 +367,8 @@ "requires the component model async builtins feature" ) +;; async function types (assert_invalid - (component (import "[async]f" (func))) - "require the component model async feature" -) - -(assert_invalid - (component (import "[async method]a.b" (func))) - "require the component model async feature" -) - -(assert_invalid - (component (import "[async static]a.b" (func))) - "require the component model async feature" + (component (import "x" (func async))) + "async component functions require the component model async feature" ) diff --git a/tests/snapshots/cli/component-model/async/names.wast.json b/tests/snapshots/cli/component-model/async/names.wast.json index 34bf1a1b78..ac16aa47f4 100644 --- a/tests/snapshots/cli/component-model/async/names.wast.json +++ b/tests/snapshots/cli/component-model/async/names.wast.json @@ -2,87 +2,32 @@ "source_filename": "tests/cli/component-model/async/names.wast", "commands": [ { - "type": "module", - "line": 3, + "type": "assert_invalid", + "line": 7, "filename": "names.0.wasm", - "module_type": "binary" + "module_type": "binary", + "text": "not in kebab case" }, { "type": "assert_invalid", - "line": 14, + "line": 13, "filename": "names.1.wasm", "module_type": "binary", - "text": "conflicts with previous name" + "text": "not in kebab case" }, { "type": "assert_invalid", - "line": 21, + "line": 19, "filename": "names.2.wasm", "module_type": "binary", - "text": "conflicts with previous name" + "text": "not in kebab case" }, { "type": "assert_invalid", - "line": 28, + "line": 26, "filename": "names.3.wasm", "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 35, - "filename": "names.4.wasm", - "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 42, - "filename": "names.5.wasm", - "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 49, - "filename": "names.6.wasm", - "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 56, - "filename": "names.7.wasm", - "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 63, - "filename": "names.8.wasm", - "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 70, - "filename": "names.9.wasm", - "module_type": "binary", - "text": "conflicts with previous name" - }, - { - "type": "assert_invalid", - "line": 77, - "filename": "names.10.wasm", - "module_type": "binary", - "text": "import name `[async method]a.a` conflicts with previous name `a`" - }, - { - "type": "assert_invalid", - "line": 84, - "filename": "names.11.wasm", - "module_type": "binary", - "text": "import name `[async static]a.a` conflicts with previous name `a`" + "text": "constructor function cannot be async" } ] } \ No newline at end of file diff --git a/tests/snapshots/cli/component-model/async/names.wast/0.print b/tests/snapshots/cli/component-model/async/names.wast/0.print deleted file mode 100644 index 77183e5f8c..0000000000 --- a/tests/snapshots/cli/component-model/async/names.wast/0.print +++ /dev/null @@ -1,10 +0,0 @@ -(component - (type (;0;) (func)) - (import "[async]f" (func (;0;) (type 0))) - (import "r" (type $r (;1;) (sub resource))) - (type (;2;) (borrow $r)) - (type (;3;) (func (param "self" 2))) - (import "[async method]r.f" (func (;1;) (type 3))) - (type (;4;) (func)) - (import "[async static]r.f2" (func (;2;) (type 4))) -) diff --git a/tests/snapshots/cli/missing-features/component-model/async.wast.json b/tests/snapshots/cli/missing-features/component-model/async.wast.json index 1748404365..6f945d4a3c 100644 --- a/tests/snapshots/cli/missing-features/component-model/async.wast.json +++ b/tests/snapshots/cli/missing-features/component-model/async.wast.json @@ -220,24 +220,10 @@ }, { "type": "assert_invalid", - "line": 371, + "line": 372, "filename": "async.31.wasm", "module_type": "binary", - "text": "require the component model async feature" - }, - { - "type": "assert_invalid", - "line": 376, - "filename": "async.32.wasm", - "module_type": "binary", - "text": "require the component model async feature" - }, - { - "type": "assert_invalid", - "line": 381, - "filename": "async.33.wasm", - "module_type": "binary", - "text": "require the component model async feature" + "text": "async component functions require the component model async feature" } ] } \ No newline at end of file