Skip to content
Open
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
142 changes: 90 additions & 52 deletions crates/wit-parser/src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,12 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> {
let mut interfaces = IndexMap::default();
let mut worlds = IndexMap::default();
let ty = &types[world];
assert_eq!(ty.imports.len(), 0);
assert_eq!(ty.exports.len(), 1);
if ty.imports.len() != 0 || ty.exports.len() != 1 {
bail!(
"cannot represent this component in WIT: expected an exported component type with \
no imports and one export"
);
}
let name = ty.exports.keys().nth(0).unwrap();
let name = decoder.decode_world(
name,
Expand Down Expand Up @@ -613,7 +617,7 @@ impl WitPackageDecoder<'_> {
) -> Result<PackageName> {
let ty = match item.ty {
ComponentEntityType::Component(ty) => &self.types[ty],
_ => unreachable!(),
_ => bail!("cannot represent this component in WIT: export `{name}` is not a world"),
};
let kebab_name = self
.parse_component_name(name)
Expand Down Expand Up @@ -788,7 +792,10 @@ impl WitPackageDecoder<'_> {
self.register_defined(id, &self.types[ty])?;
}
ComponentAnyTypeId::Resource(_) => {}
_ => unreachable!(),
_ => bail!(
"cannot represent this component in WIT: type `{name}` is a \
component, instance or function type"
),
}
let prev = self.type_map.insert(created, id);
assert!(prev.is_none());
Expand Down Expand Up @@ -1028,6 +1035,13 @@ impl WitPackageDecoder<'_> {
item: &ComponentItem,
owner: TypeOwner,
) -> Result<TypeId> {
// A WIT type has a plain name, not one like `a:b/c` or `[method]r.f`.
match self.parse_component_name(name)?.kind() {
ComponentNameKind::Plain(n) if n.is_bare() => {}
_ => bail!(
"cannot represent this component in WIT: type `{name}` doesn't have a plain name"
),
}
let (referenced, created) = match item.ty {
ComponentEntityType::Type {
referenced,
Expand Down Expand Up @@ -1069,7 +1083,10 @@ impl WitPackageDecoder<'_> {
.convert_defined(&self.types[ty])
.context("failed to convert unaliased type")?,
ComponentAnyTypeId::Resource(_) => TypeDefKind::Resource,
_ => unreachable!(),
_ => bail!(
"cannot represent this component in WIT: type `{name}` is a component, \
instance or function type"
),
}
}
};
Expand Down Expand Up @@ -1212,64 +1229,85 @@ impl WitPackageDecoder<'_> {
kind: match name.kind() {
ComponentNameKind::Plain(plain) => {
if plain.accessor.is_some() && ty.async_ {
panic!("function `{name}` is an async accessor (should be impossible)");
bail!("function `{name}` is an async accessor (should be impossible)");
}

let resource = plain
.resource()
.map(|resource| self.resources[&owner][resource.as_str()]);
match (plain.resource_func, plain.accessor) {
(None, None) => {
if ty.async_ {
FunctionKind::AsyncFreestanding
} else {
FunctionKind::Freestanding
match plain.resource_func {
None => match plain.accessor {
None => {
if ty.async_ {
FunctionKind::AsyncFreestanding
} else {
FunctionKind::Freestanding
}
}
}
(None, Some(ComponentAccessorKind::Get)) => FunctionKind::Getter,
(None, Some(ComponentAccessorKind::Set)) => FunctionKind::Setter,
Some(ComponentAccessorKind::Get) => FunctionKind::Getter,
Some(ComponentAccessorKind::Set) => FunctionKind::Setter,
},

(Some(ResourceFuncKind::Constructor), None) => {
FunctionKind::Constructor(resource.unwrap())
}
(Some(ResourceFuncKind::Constructor), Some(_)) => {
panic!(
"function `{name}` is a constructor with an accessor (should be impossible)"
);
}
Some(resource_func) => {
let Some(resource) = plain.resource() else {
bail!("function `{name}` has no resource (should be impossible)");
};
let Some(&resource) = self
.resources
.get(&owner)
.and_then(|resources| resources.get(resource.as_str()))
else {
bail!(
"cannot represent this component in WIT: function `{name}` \
is not next to the definition of its resource `{resource}`"
);
};

match (resource_func, plain.accessor) {
(ResourceFuncKind::Constructor, None) => {
FunctionKind::Constructor(resource)
}
(ResourceFuncKind::Constructor, Some(_)) => {
bail!(
"function `{name}` is a constructor with an accessor \
(should be impossible)"
);
}

(Some(ResourceFuncKind::Method), None) => {
if ty.async_ {
FunctionKind::AsyncMethod(resource.unwrap())
} else {
FunctionKind::Method(resource.unwrap())
}
}
(Some(ResourceFuncKind::Method), Some(ComponentAccessorKind::Get)) => {
FunctionKind::MethodGetter(resource.unwrap())
}
(Some(ResourceFuncKind::Method), Some(ComponentAccessorKind::Set)) => {
FunctionKind::MethodSetter(resource.unwrap())
}
(ResourceFuncKind::Method, None) => {
if ty.async_ {
FunctionKind::AsyncMethod(resource)
} else {
FunctionKind::Method(resource)
}
}
(ResourceFuncKind::Method, Some(ComponentAccessorKind::Get)) => {
FunctionKind::MethodGetter(resource)
}
(ResourceFuncKind::Method, Some(ComponentAccessorKind::Set)) => {
FunctionKind::MethodSetter(resource)
}

(Some(ResourceFuncKind::Static), None) => {
if ty.async_ {
FunctionKind::AsyncStatic(resource.unwrap())
} else {
FunctionKind::Static(resource.unwrap())
(ResourceFuncKind::Static, None) => {
if ty.async_ {
FunctionKind::AsyncStatic(resource)
} else {
FunctionKind::Static(resource)
}
}
(ResourceFuncKind::Static, Some(ComponentAccessorKind::Get)) => {
FunctionKind::StaticGetter(resource)
}
(ResourceFuncKind::Static, Some(ComponentAccessorKind::Set)) => {
FunctionKind::StaticSetter(resource)
}
}
}
(Some(ResourceFuncKind::Static), Some(ComponentAccessorKind::Get)) => {
FunctionKind::StaticGetter(resource.unwrap())
}
(Some(ResourceFuncKind::Static), Some(ComponentAccessorKind::Set)) => {
FunctionKind::StaticSetter(resource.unwrap())
}
}
}

// Functions shouldn't have ID-based names at this time.
ComponentNameKind::Interface(_) => unreachable!(),
// Functions can't have ID-based names in WIT.
ComponentNameKind::Interface(_) => bail!(
"cannot represent this component in WIT: function `{name}` has an interface's \
name"
),
},

// Note that this name includes "name mangling" such as
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
;; FAIL: component wit %
;;
;; The custom section holds the binary of:
;;
;; (component (type $c (component (export "a:b/w" (instance)))) (export "w" (type $c)))

(module
(@custom "component-type:x" "\00\61\73\6d\0d\00\01\00\07\10\01\41\02\01\42\00\04\00\05\61\3a\62\2f\77\05\00\0b\07\01\00\01\77\03\00\00\00\16\0e\63\6f\6d\70\6f\6e\65\6e\74\2d\6e\61\6d\65\01\05\03\01\00\01\63")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit wary of tests like this which are otherwise suitable for the text format but due to tooling limitations require the binary format. If you're up for it this might make sense as a new CLI flag to wasm-tools component embed to create this component, but otherwise I think it's ok to drop the binary-style tests for now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lemme see how complex this would be.

I have around 6 more PRs planned stacked on top of this.

If it looks like too much scope creep I should probably spawn out a new issue and tackle that as seperate PR.

)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: decoding custom section component-type:x

Caused by:
0: cannot represent this component in WIT: export `a:b/w` is not a world
9 changes: 9 additions & 0 deletions tests/cli/component-wit-custom-section-world-imports.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
;; FAIL: component wit %
;;
;; The custom section holds the binary of:
;;
;; (component (type $c (component (import "a:b/c" (instance)) (export "a:b/w" (component)))) (export "w" (type $c)))

(module
(@custom "component-type:x" "\00\61\73\6d\0d\00\01\00\07\1d\01\41\04\01\42\00\03\00\05\61\3a\62\2f\63\05\00\01\41\00\04\00\05\61\3a\62\2f\77\04\01\0b\07\01\00\01\77\03\00\00\00\16\0e\63\6f\6d\70\6f\6e\65\6e\74\2d\6e\61\6d\65\01\05\03\01\00\01\63")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: decoding custom section component-type:x

Caused by:
0: cannot represent this component in WIT: expected an exported component type with no imports and one export
9 changes: 9 additions & 0 deletions tests/cli/component-wit-custom-section-world-no-export.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
;; FAIL: component wit %
;;
;; The custom section holds the binary of:
;;
;; (component (type $c (component)) (export "x" (type $c)))

(module
(@custom "component-type:x" "\00\61\73\6d\0d\00\01\00\07\03\01\41\00\0b\07\01\00\01\78\03\00\00\00\16\0e\63\6f\6d\70\6f\6e\65\6e\74\2d\6e\61\6d\65\01\05\03\01\00\01\63")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: decoding custom section component-type:x

Caused by:
0: cannot represent this component in WIT: expected an exported component type with no imports and one export
5 changes: 5 additions & 0 deletions tests/cli/component-wit-func-with-interface-name.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
;; FAIL: component wit %

(component
(import "a:b/c" (func))
)
4 changes: 4 additions & 0 deletions tests/cli/component-wit-func-with-interface-name.wat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: failed to decode function from import `a:b/c`

Caused by:
0: cannot represent this component in WIT: function `a:b/c` has an interface's name
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
;; FAIL: component wit %

(component
(import "x" (implements "a:b/c") (instance
(type $t u32)
(export "t" (type (eq $t)))
))
(import "y" (implements "a:b/c") (instance
(type $i (instance))
(export "t" (type (eq $i)))
))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: failed to decode WIT from import `y`

Caused by:
0: cannot represent this component in WIT: type `t` is a component, instance or function type
6 changes: 6 additions & 0 deletions tests/cli/component-wit-import-component-type.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
;; FAIL: component wit %

(component
(type $c (component))
(import "t" (type (eq $c)))
)
4 changes: 4 additions & 0 deletions tests/cli/component-wit-import-component-type.wat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: failed to decode type from export `t`

Caused by:
0: cannot represent this component in WIT: type `t` is a component, instance or function type
8 changes: 8 additions & 0 deletions tests/cli/component-wit-instance-exports-func-type.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
;; FAIL: component wit %

(component
(import "i" (instance
(type $f (func))
(export "t" (type (eq $f)))
))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: failed to decode WIT from import `i`

Caused by:
0: failed to register type export 't'
1: cannot represent this component in WIT: type `t` is a component, instance or function type
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
;; FAIL: component wit %

(component
(import "i" (instance
(type (list u8))
(export "a:b/c@1.0.0" (type (eq 0)))
))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: failed to decode WIT from import `i`

Caused by:
0: failed to register type export 'a:b/c@1.0.0'
1: cannot represent this component in WIT: type `a:b/c@1.0.0` doesn't have a plain name
12 changes: 12 additions & 0 deletions tests/cli/component-wit-method-on-used-resource.wat

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think technically this is supposed to be an invalid component, not just an invalid-WIT component. I think it's a bug in wasmparser's valiator that this is accepted

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
;; FAIL: component wit %

(component
(import "a:b/d" (instance $d
(export "r" (type (sub resource)))
))
(alias export $d "r" (type $r))
(import "a:b/e" (instance
(export "r" (type $x (eq $r)))
(export "[method]r.f" (func (param "self" (borrow $x))))
))
)
4 changes: 4 additions & 0 deletions tests/cli/component-wit-method-on-used-resource.wat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: failed to decode WIT from import `a:b/e`

Caused by:
0: cannot represent this component in WIT: function `[method]r.f` is not next to the definition of its resource `r`
6 changes: 6 additions & 0 deletions tests/cli/component-wit-type-with-interface-name.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
;; FAIL: component wit %

(component
(type (list u8))
(import "a:b/c" (type (eq 0)))
)
4 changes: 4 additions & 0 deletions tests/cli/component-wit-type-with-interface-name.wat.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: failed to decode type from export `a:b/c`

Caused by:
0: cannot represent this component in WIT: type `a:b/c` doesn't have a plain name
Loading