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
58 changes: 34 additions & 24 deletions crates/wast/src/component/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,30 +52,22 @@ impl<'a> Parse<'a> for ComponentExternName<'a> {
} else {
parser.parse()?
};
let implements = if parser.peek2::<kw::implements>()? {
Some(parser.parens(|p| {
p.parse::<kw::implements>()?;
p.parse()
})?)
} else {
None
};
let version_suffix = if parser.peek2::<kw::version_suffix>()? {
Some(parser.parens(|p| {
p.parse::<kw::version_suffix>()?;
p.parse()
})?)
} else {
None
};
let external_id = if parser.peek2::<kw::external_id>()? {
Some(parser.parens(|p| {
p.parse::<kw::external_id>()?;
p.parse()
})?)
} else {
None
};
// Attributes are an unordered list in the text format, so accept them
// in any order, but each kind may only be specified once.
let mut implements = None;
let mut version_suffix = None;
let mut external_id = None;
loop {
if parser.peek2::<kw::implements>()? {
parse_extern_name_attribute::<kw::implements>(parser, &mut implements)?;
} else if parser.peek2::<kw::version_suffix>()? {
parse_extern_name_attribute::<kw::version_suffix>(parser, &mut version_suffix)?;
} else if parser.peek2::<kw::external_id>()? {
parse_extern_name_attribute::<kw::external_id>(parser, &mut external_id)?;
} else {
break;
}
}
Ok(ComponentExternName {
name,
implements,
Expand All @@ -85,6 +77,24 @@ impl<'a> Parse<'a> for ComponentExternName<'a> {
}
}

/// Parses a single `(<kw> "...")` attribute of a [`ComponentExternName`] into
/// `slot`, returning an error if `slot` was already filled in by a previous
/// attribute of the same kind.
fn parse_extern_name_attribute<'a, K>(parser: Parser<'a>, slot: &mut Option<&'a str>) -> Result<()>
where
K: Parse<'a> + Peek,
{
parser.parens(|p| {
let span = p.cur_span();
p.parse::<K>()?;
if slot.is_some() {
return Err(p.error_at(span, format!("duplicate {} attribute", K::display())));
}
*slot = Some(p.parse()?);
Ok(())
})
}

/// An item signature for imported items.
#[derive(Debug)]
pub struct ItemSig<'a> {
Expand Down
97 changes: 97 additions & 0 deletions tests/cli/component-model/attribute-order.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
;; RUN: wast --assert default --snapshot tests/snapshots % -f cm-canon-names,cm-implements

;; The `implements`, `version-suffix`, and `external-id` attributes on
;; import/export names are an unordered list in the text format, so they may
;; be specified in any order. Printing always emits them in a canonical order.

;; imports, all permutations
(component
(import "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance))
(import "b" (implements "a:b/c@1") (external-id "x") (version-suffix ".2.3") (instance))
(import "c" (version-suffix ".2.3") (implements "a:b/c@1") (external-id "x") (instance))
(import "d" (version-suffix ".2.3") (external-id "x") (implements "a:b/c@1") (instance))
(import "e" (external-id "x") (implements "a:b/c@1") (version-suffix ".2.3") (instance))
(import "f" (external-id "x") (version-suffix ".2.3") (implements "a:b/c@1") (instance))

;; pairs
(import "g" (external-id "x") (implements "a:b/c") (instance))
(import "h" (version-suffix ".2.3") (implements "a:b/c@1") (instance))
(import "a:b/c@1" (version-suffix ".2.3") (external-id "x") (instance))
(import "i" (external-id "x") (func))
)

;; exports, all permutations
(component
(instance $i)
(export "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(export "b" (implements "a:b/c@1") (external-id "x") (version-suffix ".2.3") (instance $i))
(export "c" (version-suffix ".2.3") (implements "a:b/c@1") (external-id "x") (instance $i))
(export "d" (version-suffix ".2.3") (external-id "x") (implements "a:b/c@1") (instance $i))
(export "e" (external-id "x") (implements "a:b/c@1") (version-suffix ".2.3") (instance $i))
(export "f" (external-id "x") (version-suffix ".2.3") (implements "a:b/c@1") (instance $i))

;; with an identifier and a type ascription
(export $g "g" (external-id "x") (implements "a:b/c") (instance $i) (instance))
)

;; instance and component type declarations
(component
(type (instance
(export "a" (external-id "x") (version-suffix ".2.3") (implements "a:b/c@1") (instance))
(export "b" (external-id "x") (implements "a:b/c") (instance))
))
(type (component
(import "a" (external-id "x") (version-suffix ".2.3") (implements "a:b/c@1") (instance))
(import "b" (version-suffix ".2.3") (implements "a:b/c@1") (instance))
(export "a" (version-suffix ".2.3") (external-id "x") (implements "a:b/c@1") (instance))
(export "b" (external-id "x") (implements "a:b/c") (instance))
))
(import "c" (instance
(export "a" (external-id "x") (implements "a:b/c") (instance))
))
)

;; instances created from inline exports
(component
(instance $a)
(instance
(export "a" (external-id "x") (version-suffix ".2.3") (implements "a:b/c@1") (instance $a))
(export "b" (external-id "x") (implements "a:b/c") (instance $a))
)
)

;; Validation still applies regardless of the order attributes are written in.
(assert_invalid
(component (import "a" (version-suffix ".1") (implements "a:b/c") (instance)))
"a version suffix requires the name to have a version")

;; Each attribute may only be specified once.
(assert_malformed
(component quote
"(import \"a\" (implements \"a:b/c\") (implements \"a:b/c\") (instance))")
"duplicate `implements` attribute")
(assert_malformed
(component quote
"(import \"a\" (implements \"a:b/c\") (external-id \"x\") (implements \"a:b/c\") (instance))")
"duplicate `implements` attribute")
(assert_malformed
(component quote
"(import \"a\" (version-suffix \".1\") (implements \"a:b/c@1\") (version-suffix \".1\") (instance))")
"duplicate `version-suffix` attribute")
(assert_malformed
(component quote
"(import \"a\" (external-id \"x\") (external-id \"y\") (instance))")
"duplicate `external-id` attribute")
(assert_malformed
(component quote
"(instance $i)"
"(export \"a\" (external-id \"x\") (implements \"a:b/c\") (external-id \"x\") (instance $i))")
"duplicate `external-id` attribute")
(assert_malformed
(component quote
"(type (instance (export \"a\" (implements \"a:b/c\") (external-id \"x\") (implements \"a:b/c\") (instance))))")
"duplicate `implements` attribute")
(assert_malformed
(component quote
"(type (component (import \"a\" (version-suffix \".1\") (implements \"a:b/c@1\") (version-suffix \".1\") (instance))))")
"duplicate `version-suffix` attribute")
85 changes: 85 additions & 0 deletions tests/snapshots/cli/component-model/attribute-order.wast.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"source_filename": "tests/cli/component-model/attribute-order.wast",
"commands": [
{
"type": "module",
"line": 8,
"filename": "attribute-order.0.wasm",
"module_type": "binary"
},
{
"type": "module",
"line": 24,
"filename": "attribute-order.1.wasm",
"module_type": "binary"
},
{
"type": "module",
"line": 38,
"filename": "attribute-order.2.wasm",
"module_type": "binary"
},
{
"type": "module",
"line": 55,
"filename": "attribute-order.3.wasm",
"module_type": "binary"
},
{
"type": "assert_invalid",
"line": 65,
"filename": "attribute-order.4.wasm",
"module_type": "binary",
"text": "a version suffix requires the name to have a version"
},
{
"type": "assert_malformed",
"line": 70,
"filename": "attribute-order.5.wat",
"module_type": "text",
"text": "duplicate `implements` attribute"
},
{
"type": "assert_malformed",
"line": 74,
"filename": "attribute-order.6.wat",
"module_type": "text",
"text": "duplicate `implements` attribute"
},
{
"type": "assert_malformed",
"line": 78,
"filename": "attribute-order.7.wat",
"module_type": "text",
"text": "duplicate `version-suffix` attribute"
},
{
"type": "assert_malformed",
"line": 82,
"filename": "attribute-order.8.wat",
"module_type": "text",
"text": "duplicate `external-id` attribute"
},
{
"type": "assert_malformed",
"line": 86,
"filename": "attribute-order.9.wat",
"module_type": "text",
"text": "duplicate `external-id` attribute"
},
{
"type": "assert_malformed",
"line": 91,
"filename": "attribute-order.10.wat",
"module_type": "text",
"text": "duplicate `implements` attribute"
},
{
"type": "assert_malformed",
"line": 95,
"filename": "attribute-order.11.wat",
"module_type": "text",
"text": "duplicate `version-suffix` attribute"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
(component
(type (;0;)
(instance)
)
(import "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;0;) (type 0)))
(type (;1;)
(instance)
)
(import "b" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;1;) (type 1)))
(type (;2;)
(instance)
)
(import "c" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;2;) (type 2)))
(type (;3;)
(instance)
)
(import "d" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;3;) (type 3)))
(type (;4;)
(instance)
)
(import "e" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;4;) (type 4)))
(type (;5;)
(instance)
)
(import "f" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;5;) (type 5)))
(type (;6;)
(instance)
)
(import "g" (implements "a:b/c") (external-id "x") (instance (;6;) (type 6)))
(type (;7;)
(instance)
)
(import "h" (implements "a:b/c@1") (version-suffix ".2.3") (instance (;7;) (type 7)))
(type (;8;)
(instance)
)
(import "a:b/c@1" (version-suffix ".2.3") (external-id "x") (instance (;8;) (type 8)))
(type (;9;) (func))
(import "i" (external-id "x") (func (;0;) (type 9)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(component
(instance $i (;0;))
(export (;1;) "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(export (;2;) "b" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(export (;3;) "c" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(export (;4;) "d" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(export (;5;) "e" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(export (;6;) "f" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $i))
(type (;0;)
(instance)
)
(export $g (;7;) "g" (implements "a:b/c") (external-id "x") (instance $i) (instance (type 0)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
(component
(type (;0;)
(instance
(type (;0;)
(instance)
)
(export (;0;) "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (type 0)))
(type (;1;)
(instance)
)
(export (;1;) "b" (implements "a:b/c") (external-id "x") (instance (type 1)))
)
)
(type (;1;)
(component
(type (;0;)
(instance)
)
(import "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (;0;) (type 0)))
(type (;1;)
(instance)
)
(import "b" (implements "a:b/c@1") (version-suffix ".2.3") (instance (;1;) (type 1)))
(type (;2;)
(instance)
)
(export (;2;) "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance (type 2)))
(type (;3;)
(instance)
)
(export (;3;) "b" (implements "a:b/c") (external-id "x") (instance (type 3)))
)
)
(type (;2;)
(instance
(type (;0;)
(instance)
)
(export (;0;) "a" (implements "a:b/c") (external-id "x") (instance (type 0)))
)
)
(import "c" (instance (;0;) (type 2)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(component
(instance $a (;0;))
(instance (;1;)
(export "a" (implements "a:b/c@1") (version-suffix ".2.3") (external-id "x") (instance $a))
(export "b" (implements "a:b/c") (external-id "x") (instance $a))
)
)
Loading