diff options
Diffstat (limited to 'third_party/rust/wast/src/component/component.rs')
-rw-r--r-- | third_party/rust/wast/src/component/component.rs | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/third_party/rust/wast/src/component/component.rs b/third_party/rust/wast/src/component/component.rs new file mode 100644 index 0000000000..8424e083b8 --- /dev/null +++ b/third_party/rust/wast/src/component/component.rs @@ -0,0 +1,313 @@ +use crate::annotation; +use crate::component::*; +use crate::kw; +use crate::parser::{Parse, Parser, Result}; +use crate::token::Index; +use crate::token::{Id, NameAnnotation, Span}; + +/// A parsed WebAssembly component module. +#[derive(Debug)] +pub struct Component<'a> { + /// Where this `component` was defined + pub span: Span, + /// An optional identifier this component is known by + pub id: Option<Id<'a>>, + /// An optional `@name` annotation for this component + pub name: Option<NameAnnotation<'a>>, + /// What kind of component this was parsed as. + pub kind: ComponentKind<'a>, +} + +/// The different kinds of ways to define a component. +#[derive(Debug)] +pub enum ComponentKind<'a> { + /// A component defined in the textual s-expression format. + Text(Vec<ComponentField<'a>>), + /// A component that had its raw binary bytes defined via the `binary` + /// directive. + Binary(Vec<&'a [u8]>), +} + +impl<'a> Component<'a> { + /// Performs a name resolution pass on this [`Component`], resolving all + /// symbolic names to indices. + /// + /// The WAT format contains a number of shorthands to make it easier to + /// write, such as inline exports, inline imports, inline type definitions, + /// etc. Additionally it allows using symbolic names such as `$foo` instead + /// of using indices. This module will postprocess an AST to remove all of + /// this syntactic sugar, preparing the AST for binary emission. This is + /// where expansion and name resolution happens. + /// + /// This function will mutate the AST of this [`Component`] and replace all + /// [`Index`](crate::token::Index) arguments with `Index::Num`. This will + /// also expand inline exports/imports listed on fields and handle various + /// other shorthands of the text format. + /// + /// If successful the AST was modified to be ready for binary encoding. + /// + /// # Errors + /// + /// If an error happens during resolution, such a name resolution error or + /// items are found in the wrong order, then an error is returned. + pub fn resolve(&mut self) -> std::result::Result<(), crate::Error> { + match &mut self.kind { + ComponentKind::Text(fields) => { + crate::component::expand::expand(fields); + } + ComponentKind::Binary(_) => {} + } + crate::component::resolve::resolve(self) + } + + /// Encodes this [`Component`] to its binary form. + /// + /// This function will take the textual representation in [`Component`] and + /// perform all steps necessary to convert it to a binary WebAssembly + /// component, suitable for writing to a `*.wasm` file. This function may + /// internally modify the [`Component`], for example: + /// + /// * Name resolution is performed to ensure that `Index::Id` isn't present + /// anywhere in the AST. + /// + /// * Inline shorthands such as imports/exports/types are all expanded to be + /// dedicated fields of the component. + /// + /// * Component fields may be shuffled around to preserve index ordering from + /// expansions. + /// + /// After all of this expansion has happened the component will be converted to + /// its binary form and returned as a `Vec<u8>`. This is then suitable to + /// hand off to other wasm runtimes and such. + /// + /// # Errors + /// + /// This function can return an error for name resolution errors and other + /// expansion-related errors. + pub fn encode(&mut self) -> std::result::Result<Vec<u8>, crate::Error> { + self.resolve()?; + Ok(crate::component::binary::encode(self)) + } + + pub(crate) fn validate(&self, parser: Parser<'_>) -> Result<()> { + let mut starts = 0; + if let ComponentKind::Text(fields) = &self.kind { + for item in fields.iter() { + if let ComponentField::Start(_) = item { + starts += 1; + } + } + } + if starts > 1 { + return Err(parser.error("multiple start sections found")); + } + Ok(()) + } +} + +impl<'a> Parse<'a> for Component<'a> { + fn parse(parser: Parser<'a>) -> Result<Self> { + let _r = parser.register_annotation("custom"); + + let span = parser.parse::<kw::component>()?.0; + let id = parser.parse()?; + let name = parser.parse()?; + + let kind = if parser.peek::<kw::binary>() { + parser.parse::<kw::binary>()?; + let mut data = Vec::new(); + while !parser.is_empty() { + data.push(parser.parse()?); + } + ComponentKind::Binary(data) + } else { + ComponentKind::Text(ComponentField::parse_remaining(parser)?) + }; + Ok(Component { + span, + id, + name, + kind, + }) + } +} + +/// A listing of all possible fields that can make up a WebAssembly component. +#[allow(missing_docs)] +#[derive(Debug)] +pub enum ComponentField<'a> { + CoreModule(CoreModule<'a>), + CoreInstance(CoreInstance<'a>), + CoreType(CoreType<'a>), + Component(NestedComponent<'a>), + Instance(Instance<'a>), + Alias(Alias<'a>), + Type(Type<'a>), + CanonicalFunc(CanonicalFunc<'a>), + CoreFunc(CoreFunc<'a>), // Supports inverted forms of other items + Func(Func<'a>), // Supports inverted forms of other items + Start(Start<'a>), + Import(ComponentImport<'a>), + Export(ComponentExport<'a>), + Custom(Custom<'a>), +} + +impl<'a> ComponentField<'a> { + fn parse_remaining(parser: Parser<'a>) -> Result<Vec<ComponentField>> { + let mut fields = Vec::new(); + while !parser.is_empty() { + fields.push(parser.parens(ComponentField::parse)?); + } + Ok(fields) + } +} + +impl<'a> Parse<'a> for ComponentField<'a> { + fn parse(parser: Parser<'a>) -> Result<Self> { + if parser.peek::<kw::core>() { + if parser.peek2::<kw::module>() { + return Ok(Self::CoreModule(parser.parse()?)); + } + if parser.peek2::<kw::instance>() { + return Ok(Self::CoreInstance(parser.parse()?)); + } + if parser.peek2::<kw::r#type>() { + return Ok(Self::CoreType(parser.parse()?)); + } + if parser.peek2::<kw::func>() { + return Ok(Self::CoreFunc(parser.parse()?)); + } + } else { + if parser.peek::<kw::component>() { + return Ok(Self::Component(parser.parse()?)); + } + if parser.peek::<kw::instance>() { + return Ok(Self::Instance(parser.parse()?)); + } + if parser.peek::<kw::alias>() { + return Ok(Self::Alias(parser.parse()?)); + } + if parser.peek::<kw::r#type>() { + return Ok(Self::Type(parser.parse()?)); + } + if parser.peek::<kw::import>() { + return Ok(Self::Import(parser.parse()?)); + } + if parser.peek::<kw::func>() { + return Ok(Self::Func(parser.parse()?)); + } + if parser.peek::<kw::export>() { + return Ok(Self::Export(parser.parse()?)); + } + if parser.peek::<kw::start>() { + return Ok(Self::Start(parser.parse()?)); + } + if parser.peek::<annotation::custom>() { + return Ok(Self::Custom(parser.parse()?)); + } + } + Err(parser.error("expected valid component field")) + } +} + +/// A function to call at instantiation time. +#[derive(Debug)] +pub struct Start<'a> { + /// The function to call. + pub func: Index<'a>, + /// The arguments to pass to the function. + pub args: Vec<ItemRef<'a, kw::value>>, + /// Names of the result values. + pub results: Vec<Option<Id<'a>>>, +} + +impl<'a> Parse<'a> for Start<'a> { + fn parse(parser: Parser<'a>) -> Result<Self> { + parser.parse::<kw::start>()?; + let func = parser.parse()?; + let mut args = Vec::new(); + while !parser.is_empty() && !parser.peek2::<kw::result>() { + args.push(parser.parens(|parser| parser.parse())?); + } + + let mut results = Vec::new(); + while !parser.is_empty() && parser.peek2::<kw::result>() { + results.push(parser.parens(|parser| { + parser.parse::<kw::result>()?; + parser.parens(|parser| { + parser.parse::<kw::value>()?; + parser.parse() + }) + })?); + } + + Ok(Start { + func, + args, + results, + }) + } +} + +/// A nested WebAssembly component. +#[derive(Debug)] +pub struct NestedComponent<'a> { + /// Where this `component` was defined + pub span: Span, + /// An optional identifier this component is known by + pub id: Option<Id<'a>>, + /// An optional `@name` annotation for this component + pub name: Option<NameAnnotation<'a>>, + /// If present, inline export annotations which indicate names this + /// definition should be exported under. + pub exports: InlineExport<'a>, + /// What kind of component this was parsed as. + pub kind: NestedComponentKind<'a>, +} + +/// The different kinds of ways to define a nested component. +#[derive(Debug)] +pub enum NestedComponentKind<'a> { + /// This is actually an inline import of a component + Import { + /// The information about where this is being imported from. + import: InlineImport<'a>, + /// The type of component being imported. + ty: ComponentTypeUse<'a, ComponentType<'a>>, + }, + /// The component is defined inline as a local definition with its fields + /// listed here. + Inline(Vec<ComponentField<'a>>), +} + +impl<'a> Parse<'a> for NestedComponent<'a> { + fn parse(parser: Parser<'a>) -> Result<Self> { + parser.depth_check()?; + + let span = parser.parse::<kw::component>()?.0; + let id = parser.parse()?; + let name = parser.parse()?; + let exports = parser.parse()?; + + let kind = if let Some(import) = parser.parse()? { + NestedComponentKind::Import { + import, + ty: parser.parse()?, + } + } else { + let mut fields = Vec::new(); + while !parser.is_empty() { + fields.push(parser.parens(|p| p.parse())?); + } + NestedComponentKind::Inline(fields) + }; + + Ok(NestedComponent { + span, + id, + name, + exports, + kind, + }) + } +} |