Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  wast.rs   Sprache: unbekannt

 
#[cfg(feature = "component-model")]
use crate::component::WastVal;
use crate::core::{WastArgCore, WastRetCore};
use crate::kw;
use crate::parser::{self, Cursor, Parse, ParseBuffer, Parser, Peek, Result};
use crate::token::{Id, Span};
use crate::{Error, Wat};

/// A parsed representation of a `*.wast` file.
///
/// WAST files are not officially specified but are used in the official test
/// suite to write official spec tests for wasm. This type represents a parsed
/// `*.wast` file which parses a list of directives in a file.
#[derive(Debug)]
pub struct Wast<'a> {
    #[allow(missing_docs)]
    pub directives: Vec<WastDirective<'a>>,
}

impl<'a> Parse<'a> for Wast<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        let mut directives = Vec::new();

        parser.with_standard_annotations_registered(|parser| {
            // If it looks like a directive token is in the stream then we parse a
            // bunch of directives, otherwise assume this is an inline module.
            if parser.peek2::<WastDirectiveToken>()? {
                while !parser.is_empty() {
                    directives.push(parser.parens(|p| p.parse())?);
                }
            } else {
                let module = parser.parse::<Wat>()?;
                directives.push(WastDirective::Module(QuoteWat::Wat(module)));
            }
            Ok(Wast { directives })
        })
    }
}

struct WastDirectiveToken;

impl Peek for WastDirectiveToken {
    fn peek(cursor: Cursor<'_>) -> Result<bool> {
        let kw = match cursor.keyword()? {
            Some((kw, _)) => kw,
            None => return Ok(false),
        };
        Ok(kw.starts_with("assert_")
            || kw == "module"
            || kw == "component"
            || kw == "register"
            || kw == "invoke")
    }

    fn display() -> &'static str {
        unimplemented!()
    }
}

/// The different kinds of directives found in a `*.wast` file.
///
///
/// Some more information about these various branches can be found at
/// <https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#scripts>.
#[allow(missing_docs)]
#[derive(Debug)]
pub enum WastDirective<'a> {
    /// The provided module is defined, validated, and then instantiated.
    Module(QuoteWat<'a>),

    /// The provided module is defined and validated.
    ///
    /// This module is not instantiated automatically.
    ModuleDefinition(QuoteWat<'a>),

    /// The named module is instantiated under the instance name provided.
    ModuleInstance {
        span: Span,
        instance: Option<Id<'a>>,
        module: Option<Id<'a>>,
    },

    /// Asserts the module cannot be decoded with the given error.
    AssertMalformed {
        span: Span,
        module: QuoteWat<'a>,
        message: &'a str,
    },

    /// Asserts the module cannot be validated with the given error.
    AssertInvalid {
        span: Span,
        module: QuoteWat<'a>,
        message: &'a str,
    },

    /// Registers the `module` instance with the given `name` to be available
    /// for importing in future module instances.
    Register {
        span: Span,
        name: &'a str,
        module: Option<Id<'a>>,
    },

    /// Invokes the specified export.
    Invoke(WastInvoke<'a>),

    /// The invocation provided should trap with the specified error.
    AssertTrap {
        span: Span,
        exec: WastExecute<'a>,
        message: &'a str,
    },

    /// The invocation provided should succeed with the specified results.
    AssertReturn {
        span: Span,
        exec: WastExecute<'a>,
        results: Vec<WastRet<'a>>,
    },

    /// The invocation provided should exhaust system resources (e.g. stack
    /// overflow).
    AssertExhaustion {
        span: Span,
        call: WastInvoke<'a>,
        message: &'a str,
    },

    /// The provided module should fail to link when instantiation is attempted.
    AssertUnlinkable {
        span: Span,
        module: Wat<'a>,
        message: &'a str,
    },

    /// The invocation provided should throw an exception.
    AssertException { span: Span, exec: WastExecute<'a> },

    /// The invocation should fail to handle a suspension.
    AssertSuspension {
        span: Span,
        exec: WastExecute<'a>,
        message: &'a str,
    },

    /// Creates a new system thread which executes the given commands.
    Thread(WastThread<'a>),

    /// Waits for the specified thread to exit.
    Wait { span: Span, thread: Id<'a> },
}

impl WastDirective<'_> {
    /// Returns the location in the source that this directive was defined at
    pub fn span(&self) -> Span {
        match self {
            WastDirective::Module(QuoteWat::Wat(w))
            | WastDirective::ModuleDefinition(QuoteWat::Wat(w)) => w.span(),
            WastDirective::Module(QuoteWat::QuoteModule(span, _))
            | WastDirective::ModuleDefinition(QuoteWat::QuoteModule(span, _)) => *span,
            WastDirective::Module(QuoteWat::QuoteComponent(span, _))
            | WastDirective::ModuleDefinition(QuoteWat::QuoteComponent(span, _)) => *span,
            WastDirective::ModuleInstance { span, .. }
            | WastDirective::AssertMalformed { span, .. }
            | WastDirective::Register { span, .. }
            | WastDirective::AssertTrap { span, .. }
            | WastDirective::AssertReturn { span, .. }
            | WastDirective::AssertExhaustion { span, .. }
            | WastDirective::AssertUnlinkable { span, .. }
            | WastDirective::AssertInvalid { span, .. }
            | WastDirective::AssertException { span, .. }
            | WastDirective::AssertSuspension { span, .. }
            | WastDirective::Wait { span, .. } => *span,
            WastDirective::Invoke(i) => i.span,
            WastDirective::Thread(t) => t.span,
        }
    }
}

impl<'a> Parse<'a> for WastDirective<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        let mut l = parser.lookahead1();
        if l.peek::<kw::module>()? || l.peek::<kw::component>()? {
            parse_wast_module(parser)
        } else if l.peek::<kw::assert_malformed>()? {
            let span = parser.parse::<kw::assert_malformed>()?.0;
            Ok(WastDirective::AssertMalformed {
                span,
                module: parser.parens(|p| p.parse())?,
                message: parser.parse()?,
            })
        } else if l.peek::<kw::assert_invalid>()? {
            let span = parser.parse::<kw::assert_invalid>()?.0;
            Ok(WastDirective::AssertInvalid {
                span,
                module: parser.parens(|p| p.parse())?,
                message: parser.parse()?,
            })
        } else if l.peek::<kw::register>()? {
            let span = parser.parse::<kw::register>()?.0;
            Ok(WastDirective::Register {
                span,
                name: parser.parse()?,
                module: parser.parse()?,
            })
        } else if l.peek::<kw::invoke>()? {
            Ok(WastDirective::Invoke(parser.parse()?))
        } else if l.peek::<kw::assert_trap>()? {
            let span = parser.parse::<kw::assert_trap>()?.0;
            Ok(WastDirective::AssertTrap {
                span,
                exec: parser.parens(|p| p.parse())?,
                message: parser.parse()?,
            })
        } else if l.peek::<kw::assert_return>()? {
            let span = parser.parse::<kw::assert_return>()?.0;
            let exec = parser.parens(|p| p.parse())?;
            let mut results = Vec::new();
            while !parser.is_empty() {
                results.push(parser.parens(|p| p.parse())?);
            }
            Ok(WastDirective::AssertReturn {
                span,
                exec,
                results,
            })
        } else if l.peek::<kw::assert_exhaustion>()? {
            let span = parser.parse::<kw::assert_exhaustion>()?.0;
            Ok(WastDirective::AssertExhaustion {
                span,
                call: parser.parens(|p| p.parse())?,
                message: parser.parse()?,
            })
        } else if l.peek::<kw::assert_unlinkable>()? {
            let span = parser.parse::<kw::assert_unlinkable>()?.0;
            Ok(WastDirective::AssertUnlinkable {
                span,
                module: parser.parens(parse_wat)?,
                message: parser.parse()?,
            })
        } else if l.peek::<kw::assert_exception>()? {
            let span = parser.parse::<kw::assert_exception>()?.0;
            Ok(WastDirective::AssertException {
                span,
                exec: parser.parens(|p| p.parse())?,
            })
        } else if l.peek::<kw::assert_suspension>()? {
            let span = parser.parse::<kw::assert_suspension>()?.0;
            Ok(WastDirective::AssertSuspension {
                span,
                exec: parser.parens(|p| p.parse())?,
                message: parser.parse()?,
            })
        } else if l.peek::<kw::thread>()? {
            Ok(WastDirective::Thread(parser.parse()?))
        } else if l.peek::<kw::wait>()? {
            let span = parser.parse::<kw::wait>()?.0;
            Ok(WastDirective::Wait {
                span,
                thread: parser.parse()?,
            })
        } else {
            Err(l.error())
        }
    }
}

#[allow(missing_docs)]
#[derive(Debug)]
pub enum WastExecute<'a> {
    Invoke(WastInvoke<'a>),
    Wat(Wat<'a>),
    Get {
        span: Span,
        module: Option<Id<'a>>,
        global: &'a str,
    },
}

impl<'a> WastExecute<'a> {
    /// Returns the first span for this execute statement.
    pub fn span(&self) -> Span {
        match self {
            WastExecute::Invoke(i) => i.span,
            WastExecute::Wat(i) => i.span(),
            WastExecute::Get { span, .. } => *span,
        }
    }
}

impl<'a> Parse<'a> for WastExecute<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        let mut l = parser.lookahead1();
        if l.peek::<kw::invoke>()? {
            Ok(WastExecute::Invoke(parser.parse()?))
        } else if l.peek::<kw::module>()? || l.peek::<kw::component>()? {
            Ok(WastExecute::Wat(parse_wat(parser)?))
        } else if l.peek::<kw::get>()? {
            let span = parser.parse::<kw::get>()?.0;
            Ok(WastExecute::Get {
                span,
                module: parser.parse()?,
                global: parser.parse()?,
            })
        } else {
            Err(l.error())
        }
    }
}

fn parse_wat(parser: Parser) -> Result<Wat> {
    // Note that this doesn't use `Parse for Wat` since the `parser` provided
    // has already peeled back the first layer of parentheses while `Parse for
    // Wat` expects to be the top layer which means it also tries to peel off
    // the parens. Instead we can skip the sugar that `Wat` has for simply a
    // list of fields (no `(module ...)` container) and just parse the `Module`
    // itself.
    if parser.peek::<kw::component>()? {
        Ok(Wat::Component(parser.parse()?))
    } else {
        Ok(Wat::Module(parser.parse()?))
    }
}

#[allow(missing_docs)]
#[derive(Debug)]
pub struct WastInvoke<'a> {
    pub span: Span,
    pub module: Option<Id<'a>>,
    pub name: &'a str,
    pub args: Vec<WastArg<'a>>,
}

impl<'a> Parse<'a> for WastInvoke<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        let span = parser.parse::<kw::invoke>()?.0;
        let module = parser.parse()?;
        let name = parser.parse()?;
        let mut args = Vec::new();
        while !parser.is_empty() {
            args.push(parser.parens(|p| p.parse())?);
        }
        Ok(WastInvoke {
            span,
            module,
            name,
            args,
        })
    }
}

fn parse_wast_module<'a>(parser: Parser<'a>) -> Result<WastDirective<'a>> {
    if parser.peek2::<kw::quote>()? {
        QuoteWat::parse(parser).map(WastDirective::Module)
    } else if parser.peek2::<kw::definition>()? {
        fn parse_module(span: Span, parser: Parser<'_>) -> Result<Wat<'_>> {
            Ok(Wat::Module(
                crate::core::Module::parse_without_module_keyword(span, parser)?,
            ))
        }
        fn parse_component(_span: Span, parser: Parser<'_>) -> Result<Wat<'_>> {
            #[cfg(feature = "component-model")]
            return Ok(Wat::Component(
                crate::component::Component::parse_without_component_keyword(_span, parser)?,
            ));
            #[cfg(not(feature = "component-model"))]
            return Err(parser.error("component model support disabled at compile time"));
        }
        let (span, ctor) = if parser.peek::<kw::component>()? {
            (
                parser.parse::<kw::component>()?.0,
                parse_component as fn(_, _) -> _,
            )
        } else {
            (
                parser.parse::<kw::module>()?.0,
                parse_module as fn(_, _) -> _,
            )
        };
        parser.parse::<kw::definition>()?;
        Ok(WastDirective::ModuleDefinition(QuoteWat::Wat(ctor(
            span, parser,
        )?)))
    } else if parser.peek2::<kw::instance>()? {
        let span = if parser.peek::<kw::component>()? {
            parser.parse::<kw::component>()?.0
        } else {
            parser.parse::<kw::module>()?.0
        };
        parser.parse::<kw::instance>()?;
        Ok(WastDirective::ModuleInstance {
            span,
            instance: parser.parse()?,
            module: parser.parse()?,
        })
    } else {
        QuoteWat::parse(parser).map(WastDirective::Module)
    }
}

#[allow(missing_docs)]
#[derive(Debug)]
pub enum QuoteWat<'a> {
    Wat(Wat<'a>),
    QuoteModule(Span, Vec<(Span, &'a [u8])>),
    QuoteComponent(Span, Vec<(Span, &'a [u8])>),
}

impl<'a> QuoteWat<'a> {
    /// Encodes this module to bytes, either by encoding the module directly or
    /// parsing the contents and then encoding it.
    pub fn encode(&mut self) -> Result<Vec<u8>, Error> {
        match self.to_test()? {
            QuoteWatTest::Binary(bytes) => Ok(bytes),
            QuoteWatTest::Text(text) => {
                let text = std::str::from_utf8(&text).map_err(|_| {
                    let span = self.span();
                    Error::new(span, "malformed UTF-8 encoding".to_string())
                })?;
                let buf = ParseBuffer::new(&text)?;
                let mut wat = parser::parse::<Wat<'_>>(&buf)?;
                wat.encode()
            }
        }
    }

    /// Converts this to either a `QuoteWatTest::Binary` or
    /// `QuoteWatTest::Text` depending on what it is internally.
    pub fn to_test(&mut self) -> Result<QuoteWatTest, Error> {
        let (source, prefix) = match self {
            QuoteWat::Wat(m) => return m.encode().map(QuoteWatTest::Binary),
            QuoteWat::QuoteModule(_, source) => (source, None),
            QuoteWat::QuoteComponent(_, source) => (source, Some("(component")),
        };
        let mut ret = Vec::new();
        for (_, src) in source {
            ret.extend_from_slice(src);
            ret.push(b' ');
        }
        if let Some(prefix) = prefix {
            ret.splice(0..0, prefix.as_bytes().iter().copied());
            ret.push(b')');
        }
        Ok(QuoteWatTest::Text(ret))
    }

    /// Returns the identifier, if registered, for this module.
    pub fn name(&self) -> Option<Id<'a>> {
        match self {
            QuoteWat::Wat(Wat::Module(m)) => m.id,
            QuoteWat::Wat(Wat::Component(m)) => m.id,
            QuoteWat::QuoteModule(..) | QuoteWat::QuoteComponent(..) => None,
        }
    }

    /// Returns the defining span of this module.
    pub fn span(&self) -> Span {
        match self {
            QuoteWat::Wat(w) => w.span(),
            QuoteWat::QuoteModule(span, _) => *span,
            QuoteWat::QuoteComponent(span, _) => *span,
        }
    }
}

impl<'a> Parse<'a> for QuoteWat<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        if parser.peek2::<kw::quote>()? {
            let ctor = if parser.peek::<kw::component>()? {
                parser.parse::<kw::component>()?;
                QuoteWat::QuoteComponent
            } else {
                parser.parse::<kw::module>()?;
                QuoteWat::QuoteModule
            };
            let span = parser.parse::<kw::quote>()?.0;
            let mut src = Vec::new();
            while !parser.is_empty() {
                let span = parser.cur_span();
                let string = parser.parse()?;
                src.push((span, string));
            }
            Ok(ctor(span, src))
        } else {
            Ok(QuoteWat::Wat(parse_wat(parser)?))
        }
    }
}

/// Returned from [`QuoteWat::to_test`].
#[allow(missing_docs)]
#[derive(Debug)]
pub enum QuoteWatTest {
    Binary(Vec<u8>),
    Text(Vec<u8>),
}

#[derive(Debug)]
#[allow(missing_docs)]
pub enum WastArg<'a> {
    Core(WastArgCore<'a>),
    // TODO: technically this isn't cargo-compliant since it means that this
    // isn't and additive feature by defining this conditionally. That being
    // said this seems unlikely to break many in practice so this isn't a shared
    // type, so fixing this is left to a future commit.
    #[cfg(feature = "component-model")]
    Component(WastVal<'a>),
}

impl<'a> Parse<'a> for WastArg<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        #[cfg(feature = "component-model")]
        if parser.peek::<WastArgCore<'_>>()? {
            Ok(WastArg::Core(parser.parse()?))
        } else {
            Ok(WastArg::Component(parser.parse()?))
        }

        #[cfg(not(feature = "component-model"))]
        Ok(WastArg::Core(parser.parse()?))
    }
}

#[derive(Debug)]
#[allow(missing_docs)]
pub enum WastRet<'a> {
    Core(WastRetCore<'a>),
    #[cfg(feature = "component-model")]
    Component(WastVal<'a>),
}

impl<'a> Parse<'a> for WastRet<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        #[cfg(feature = "component-model")]
        if parser.peek::<WastRetCore<'_>>()? {
            Ok(WastRet::Core(parser.parse()?))
        } else {
            Ok(WastRet::Component(parser.parse()?))
        }

        #[cfg(not(feature = "component-model"))]
        Ok(WastRet::Core(parser.parse()?))
    }
}

#[derive(Debug)]
#[allow(missing_docs)]
pub struct WastThread<'a> {
    pub span: Span,
    pub name: Id<'a>,
    pub shared_module: Option<Id<'a>>,
    pub directives: Vec<WastDirective<'a>>,
}

impl<'a> Parse<'a> for WastThread<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        parser.depth_check()?;
        let span = parser.parse::<kw::thread>()?.0;
        let name = parser.parse()?;

        let shared_module = if parser.peek2::<kw::shared>()? {
            let name = parser.parens(|p| {
                p.parse::<kw::shared>()?;
                p.parens(|p| {
                    p.parse::<kw::module>()?;
                    p.parse()
                })
            })?;
            Some(name)
        } else {
            None
        };
        let mut directives = Vec::new();
        while !parser.is_empty() {
            directives.push(parser.parens(|p| p.parse())?);
        }
        Ok(WastThread {
            span,
            name,
            shared_module,
            directives,
        })
    }
}

[ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge