Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
29 changes: 28 additions & 1 deletion crates/wasm-encoder/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,21 +353,44 @@ 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<u8>,
}

impl<'a> ComponentFuncTypeEncoder<'a> {
fn new(sink: &'a mut Vec<u8>) -> 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.
Expand All @@ -383,6 +406,9 @@ impl<'a> ComponentFuncTypeEncoder<'a> {
T: Into<ComponentValType>,
{
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);
Expand All @@ -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<ComponentValType>) -> &mut Self {
assert!(self.async_encoded);
assert!(self.params_encoded);
assert!(!self.results_encoded);
self.results_encoded = true;
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-encoder/src/reencode/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ pub mod component_utils {
mut func: crate::ComponentFuncTypeEncoder<'_>,
ty: wasmparser::ComponentFuncType<'_>,
) -> Result<(), Error<T::Error>> {
func.async_(ty.async_);
func.params(
Vec::from(ty.params)
.into_iter()
Expand Down
10 changes: 8 additions & 2 deletions crates/wasmparser/src/readers/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<_>>()?;
let result = read_resultlist(reader)?;
ComponentType::Func(ComponentFuncType { params, result })
ComponentType::Func(ComponentFuncType {
async_: byte == 0x43,
params,
result,
})
}
0x41 => ComponentType::Component(
reader
Expand Down Expand Up @@ -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.
Expand Down
36 changes: 15 additions & 21 deletions crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,7 @@ impl ComponentState {
}

let func_ty = ComponentFuncType {
async_: false,
info: TypeInfo::new(),
params: result
.iter()
Expand Down Expand Up @@ -3051,6 +3052,13 @@ impl ComponentState {
) -> Result<ComponentFuncType> {
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(),
Expand Down Expand Up @@ -3092,6 +3100,7 @@ impl ComponentState {
.transpose()?;

Ok(ComponentFuncType {
async_: ty.async_,
info,
params,
result,
Expand Down Expand Up @@ -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(_) => {}

Expand All @@ -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
Expand Down Expand Up @@ -4593,7 +4599,6 @@ impl ComponentNameContext {
ty: &ComponentEntityType,
types: &TypeAlloc,
offset: usize,
features: &WasmFeatures,
) -> Result<()> {
let func = || {
let id = match ty {
Expand All @@ -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(_)
Expand All @@ -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"),
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
11 changes: 11 additions & 0 deletions crates/wasmparser/src/validator/component_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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`
Expand Down
Loading