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>>>;