Skip to main content

wain_validate/
error.rs

1use std::borrow::Cow;
2use std::fmt;
3use wain_ast::source::Source;
4use wain_ast::*;
5
6#[cfg_attr(test, derive(Debug))]
7pub enum ErrorKind {
8    IndexOutOfBounds {
9        idx: u32,
10        upper: usize,
11        what: &'static str,
12    },
13    MultipleReturnTypes(Vec<ValType>),
14    TypeMismatch {
15        expected: Option<ValType>,
16        actual: Option<ValType>,
17    },
18    CtrlFrameEmpty {
19        op: &'static str,
20        frame_start: usize,
21        idx_in_op_stack: usize,
22    },
23    SetImmutableGlobal {
24        ty: ValType,
25        idx: u32,
26    },
27    TooLargeAlign {
28        align: u32,
29        bits: u32,
30    },
31    InvalidLimitRange(u32, u32),
32    LimitsOutOfRange {
33        value: u32,
34        min: u32,
35        max: u32,
36        what: &'static str,
37    },
38    NotConstantInstruction(&'static str),
39    NoInstructionForConstant,
40    TooManyInstructionForConstant(usize),
41    MutableForConstant(u32),
42    StartFunctionSignature {
43        idx: u32,
44        params: Vec<ValType>,
45        results: Vec<ValType>,
46    },
47    MultipleTables(usize),
48    MultipleMemories(usize),
49    AlreadyExported {
50        name: String,
51        prev_offset: usize,
52    },
53    MemoryIsNotDefined,
54    InvalidStackDepth {
55        expected: usize,
56        actual: usize,
57        remaining: String,
58    },
59}
60
61#[cfg_attr(test, derive(Debug))]
62pub struct Error<S: Source> {
63    kind: ErrorKind,
64    source: S,
65    offset: usize,
66    pub(crate) when: Cow<'static, str>,
67}
68
69impl<S: Source> Error<S> {
70    pub(crate) fn update_msg(mut self: Box<Self>, new_msg: String) -> Box<Self> {
71        self.when = Cow::Owned(new_msg);
72        self
73    }
74    pub fn kind(&self) -> &ErrorKind {
75        &self.kind
76    }
77}
78
79pub(crate) struct Ordinal(pub(crate) usize);
80impl fmt::Display for Ordinal {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        match self.0 % 10 {
83            1 => write!(f, "{}st", self.0),
84            2 => write!(f, "{}nd", self.0),
85            3 => write!(f, "{}rd", self.0),
86            _ => write!(f, "{}th", self.0),
87        }
88    }
89}
90
91impl<S: Source> fmt::Display for Error<S> {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        use ErrorKind::*;
94        match &self.kind {
95            IndexOutOfBounds { idx, upper, what } => write!(f, "{} index {} out of bounds 0 <= idx < {}", what, idx, upper)?,
96            MultipleReturnTypes(tys) => {
97                let ss = tys.iter().map(AsRef::as_ref).collect::<Vec<&str>>();
98                write!(f, "multiple return types are not allowed for now but got [{}]", ss.join(", "))?
99            }
100            TypeMismatch { expected, actual } => {
101                assert_ne!(expected, actual);
102                match expected {
103                    Some(t) => write!(f, "expected type '{}'", t)?,
104                    None => write!(f, "expected no type")?,
105                }
106                match actual {
107                    Some(t) => write!(f, "but got type '{}'", t)?,
108                    None => write!(f, "but got no type")?,
109                }
110            }
111            CtrlFrameEmpty { op, frame_start, idx_in_op_stack: 0 } => write!(
112                f,
113                "operand stack cannot be empty at '{}' instruction while validating instruction sequence starting at offset {}",
114                op, frame_start
115            )?,
116            CtrlFrameEmpty { op, frame_start, idx_in_op_stack } => write!(
117                f,
118                "empty control frame cannot be empty at '{}' instruction. the frame started at byte offset {} and top of \
119                 control frame is op_stack[{}]",
120                op, frame_start, idx_in_op_stack
121            )?,
122            SetImmutableGlobal { ty, idx } => write!(f, "{} value cannot be set to immutable global variable {}", ty, idx)?,
123            TooLargeAlign { align, bits } => write!(f, "align {} must not be larger than {}bits / 8", align, bits)?,
124            InvalidLimitRange(min, max) => write!(f, "range for limits {}..{} is invalid", min, max)?,
125            LimitsOutOfRange { value, min, max, what } => write!(f, "limit {} is out of range {}..{} at {}", value, min, max, what)?,
126            NotConstantInstruction(op) => write!(f, "instruction '{}' is not valid for constant. only 'global.get' or '*.const' are valid in constant expressions", op)?,
127            NoInstructionForConstant => write!(f, "at least one instruction is necessary for constant expressions")?,
128            TooManyInstructionForConstant(len) => write!(f, "exactly one instruction is allowed for constant expressions but {} instructions found", len)?,
129            MutableForConstant(idx) => write!(f, "constant expressions cannot reference mutable global variable {}", idx)?,
130            StartFunctionSignature { idx, params, results } => write!(
131                f,
132                "start function should have no parameter and no result [] -> [] but found function '{}' is [{}] -> [{}]",
133                idx,
134                params.iter().map(AsRef::<str>::as_ref).collect::<Vec<_>>().join(" "),
135                results.iter().map(AsRef::<str>::as_ref).collect::<Vec<_>>().join(" "),
136            )?,
137            MultipleTables(size) => write!(f, "number of tables must not be larger than 1 but got {}", size)?,
138            MultipleMemories(size) => write!(f, "number of memories must not be larger than 1 but got {}", size)?,
139            AlreadyExported { name, prev_offset } => write!(f, "'{}' was already exported at offset {}", name, prev_offset)?,
140            MemoryIsNotDefined => write!(f, "at least one memory section must be defined")?,
141            InvalidStackDepth { expected, actual, remaining } => write!(f, "expected operand stack depth is {} but actually {} with {} remaining on the stack", expected, actual, remaining,)?,
142        }
143
144        write!(f, ". error while validating {}. ", self.when)?;
145
146        self.source.describe(f, self.offset)
147    }
148}
149
150impl<S: Source> Error<S> {
151    pub(crate) fn new(kind: ErrorKind, when: Cow<'static, str>, offset: usize, source: &S) -> Box<Self> {
152        Box::new(Self {
153            kind,
154            source: source.clone(),
155            offset,
156            when,
157        })
158    }
159
160    pub fn source(&self) -> &S {
161        &self.source
162    }
163
164    pub fn offset(&self) -> usize {
165        self.offset
166    }
167}
168
169pub type Result<T, S> = ::std::result::Result<T, Box<Error<S>>>;