Skip to content
Closed
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
61 changes: 44 additions & 17 deletions crates/wit-parser/src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,10 @@ impl ComponentInfo {
Chunk::NeedMoreData(hint) => {
assert!(!eof); // otherwise an error would be returned

// Use the hint to preallocate more space, then read
// some more data into our buffer.
//
// Note that the buffer management here is not ideal,
// but it's compact enough to fit in an example!
let len = buffer.len();
buffer.extend((0..hint).map(|_| 0u8));
let n = reader.read(&mut buffer[len..])?;
buffer.truncate(len + n);
// Read up to the hint into our buffer, which grows with
// what is read: the hint comes from the input, so it may
// be far more than there is.
let n = (&mut reader).take(hint as u64).read_to_end(&mut buffer)?;
eof = n == 0;
continue;
}
Expand Down Expand Up @@ -470,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 @@ -618,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 @@ -793,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 @@ -1033,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 @@ -1074,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 @@ -1222,7 +1234,19 @@ impl WitPackageDecoder<'_> {

let resource = plain
.resource()
.map(|resource| self.resources[&owner][resource.as_str()]);
.map(|resource| {
self.resources
.get(&owner)
.and_then(|resources| resources.get(resource.as_str()))
.copied()
.with_context(|| {
format!(
"cannot represent this component in WIT: function \
`{name}` is not next to the definition of its resource"
)
})
})
.transpose()?;
match (plain.resource_func, plain.accessor) {
(None, None) => {
if ty.async_ {
Expand Down Expand Up @@ -1273,8 +1297,11 @@ impl WitPackageDecoder<'_> {
}
}

// 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
34 changes: 22 additions & 12 deletions crates/wit-parser/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4442,20 +4442,30 @@ impl Remap {
}

fn type_has_borrow(&mut self, resolve: &Resolve, ty: &Type) -> bool {
let id = match ty {
Type::Id(id) => *id,
_ => return false,
// A chain of aliases is as long as the source makes it, so it is walked
// here and not recursed down.
let mut ty = *ty;
let mut walked = Vec::new();
let result = loop {
let id = match ty {
Type::Id(id) => id,
_ => break false,
};
if let Some(Some(has_borrow)) = self.type_has_borrow.get(id.index()) {
break *has_borrow;
}
walked.push(id);
match &resolve.types[id].kind {
TypeDefKind::Type(next) => ty = *next,
_ => break self.typedef_has_borrow(resolve, &resolve.types[id]),
}
};

if let Some(Some(has_borrow)) = self.type_has_borrow.get(id.index()) {
return *has_borrow;
}

let result = self.typedef_has_borrow(resolve, &resolve.types[id]);
if self.type_has_borrow.len() <= id.index() {
self.type_has_borrow.resize(id.index() + 1, None);
for id in walked {
if self.type_has_borrow.len() <= id.index() {
self.type_has_borrow.resize(id.index() + 1, None);
}
self.type_has_borrow[id.index()] = Some(result);
}
self.type_has_borrow[id.index()] = Some(result);
result
}

Expand Down
53 changes: 53 additions & 0 deletions crates/wit-parser/tests/alias_chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! A chain of type aliases is as long as the source makes it, and how deep the
//! parser recurses must not depend on that: a stack overflow aborts the process.

use wit_parser::Resolve;

fn chain(aliases: usize) -> String {
let mut wit = String::from("package a:b;\ninterface i {\n type t0 = u32;\n");
for i in 1..aliases {
wit.push_str(&format!(" type t{i} = t{};\n", i - 1));
}
let last = aliases - 1;
wit.push_str(&format!(
" get: func(x: t{last}) -> t{last};\n}}\nworld w {{ import i; }}\n"
));
wit
}

#[test]
fn a_long_alias_chain_parses_on_a_small_stack() {
let wit = chain(200_000);
let parse = move || {
let mut resolve = Resolve::default();
resolve.push_str("chain.wit", &wit).unwrap();
};
// Without threads, as on wasm32-wasip1, parse on the main thread, whose
// stack is as small as the one asked for here.
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
parse();
} else {
std::thread::Builder::new()
.stack_size(1 << 20)
.spawn(parse)
.unwrap()
.join()
.unwrap();
}
}

#[test]
fn a_borrow_at_the_end_of_an_alias_chain_is_still_found() {
let wit = "package a:b;
interface i {
resource r;
type b = borrow<r>;
type c = b;
type d = c;
get: func() -> d;
other: func() -> d;
}";
let mut resolve = Resolve::default();
let err = resolve.push_str("borrow.wit", wit).unwrap_err();
assert!(format!("{err:#}").contains("borrow<T>"), "{err:#}");
}
38 changes: 38 additions & 0 deletions crates/wit-parser/tests/decode_allocation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! A length in the input must not decide how much the decoder allocates.
#![cfg(feature = "decoding")]
#![allow(unsafe_code)]

use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};

struct Largest;

static LARGEST: AtomicUsize = AtomicUsize::new(0);

unsafe impl GlobalAlloc for Largest {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
LARGEST.fetch_max(layout.size(), Ordering::Relaxed);
unsafe { System.alloc(layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, size: usize) -> *mut u8 {
LARGEST.fetch_max(size, Ordering::Relaxed);
unsafe { System.realloc(ptr, layout, size) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
}

#[global_allocator]
static ALLOCATOR: Largest = Largest;

#[test]
fn a_section_claiming_4_gib_allocates_little() {
// A component's header, then a custom section claiming nearly 4 GiB.
let bytes = [
0x00, 0x61, 0x73, 0x6d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f,
];
assert!(wit_parser::decoding::decode(&bytes).is_err());
let largest = LARGEST.load(Ordering::Relaxed);
assert!(largest < 1 << 20, "allocated {largest} bytes");
}
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")
)
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
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
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