1use crate::value::Value;
2use std::fmt;
3use wain_ast::{Import, ValType};
4
5#[cfg_attr(test, derive(Debug))]
6pub enum TrapReason {
7 UnknownImport {
8 mod_name: String,
9 name: String,
10 kind: &'static str,
11 },
12 OutOfLimit {
13 max: usize,
14 idx: usize,
15 kind: &'static str,
16 },
17 DataSegmentOutOfBuffer {
18 segment_end: usize,
19 buffer_size: usize,
20 },
21 ElemSegmentLargerThanTable {
22 segment_end: usize,
23 table_size: usize,
24 },
25 ReachUnreachable,
26 IdxOutOfTable {
27 idx: usize,
28 table_size: usize,
29 },
30 UninitializedElem(usize),
31 FuncSignatureMismatch {
32 import: Option<(String, String)>,
33 expected_params: Vec<ValType>,
34 expected_results: Vec<ValType>,
35 actual_params: Vec<ValType>,
36 actual_results: Vec<ValType>,
37 },
38 LoadMemoryOutOfRange {
40 max: usize,
41 addr: usize,
42 operation: &'static str,
43 ty: &'static str,
44 },
45 ImportFuncCallFail {
46 mod_name: String,
47 name: String,
48 msg: String,
49 },
50 WrongInvokeTarget {
51 name: String,
52 actual: Option<&'static str>,
53 },
54 InvokeInvalidArgs {
55 name: String,
56 args: Vec<Value>,
57 arg_types: Vec<ValType>,
58 },
59 RemZeroDivisor,
60 DivByZeroOrOverflow,
61 ValueOutOfRange {
62 src_val: Value,
63 dest_type: &'static str,
64 },
65}
66
67#[cfg_attr(test, derive(Debug))]
68pub struct Trap {
69 pub reason: TrapReason,
70 pub offset: usize,
71}
72
73impl Trap {
74 pub(crate) fn unknown_import(import: &Import<'_>, kind: &'static str, offset: usize) -> Box<Self> {
75 Self::new(
76 TrapReason::UnknownImport {
77 mod_name: import.mod_name.0.to_string(),
78 name: import.name.0.to_string(),
79 kind,
80 },
81 offset,
82 )
83 }
84
85 pub(crate) fn out_of_range<N: Into<Value>>(num: N, type_name: &'static str, offset: usize) -> Box<Self> {
86 Self::new(
87 TrapReason::ValueOutOfRange {
88 src_val: num.into(),
89 dest_type: type_name,
90 },
91 offset,
92 )
93 }
94
95 pub(crate) fn new(reason: TrapReason, offset: usize) -> Box<Trap> {
96 Box::new(Trap { reason, offset })
97 }
98}
99
100struct JoinWritable<'a, D: fmt::Display>(&'a [D], &'static str);
101
102impl<'a, D: fmt::Display> fmt::Display for JoinWritable<'a, D> {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 if let Some(d) = self.0.first() {
105 d.fmt(f)?;
106 }
107 for d in self.0.iter().skip(1) {
108 write!(f, "{}{}", self.1, d)?;
109 }
110 Ok(())
111 }
112}
113
114impl fmt::Display for Trap {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 use TrapReason::*;
117 match &self.reason {
118 UnknownImport { mod_name, name, kind } => write!(
119 f,
120 "unknown module '{}' or unknown {} value '{}' imported from the module",
121 mod_name, kind, name,
122 )?,
123 OutOfLimit { max, idx, kind } => {
124 write!(f, "specified {} index 0x{:x} is out of limit 0x{:x}", kind, idx, max,)?
125 }
126 DataSegmentOutOfBuffer {
127 segment_end,
128 buffer_size,
129 } => write!(
130 f,
131 "'data' segment ends at address 0x{:x} but memory buffer size is 0x{:x}",
132 segment_end, buffer_size,
133 )?,
134 ElemSegmentLargerThanTable {
135 segment_end,
136 table_size,
137 } => write!(
138 f,
139 "'elem' segment ends at index {} but table length is {}",
140 segment_end, table_size,
141 )?,
142 ReachUnreachable => f.write_str("reached unreachable code")?,
143 IdxOutOfTable { idx, table_size } => write!(
144 f,
145 "cannot refer function because index {} is out of table size {}",
146 idx, table_size
147 )?,
148 UninitializedElem(idx) => write!(f, "element at index {} in table is uninitialized", idx,)?,
149 FuncSignatureMismatch {
150 import,
151 expected_params,
152 expected_results,
153 actual_params,
154 actual_results,
155 } => {
156 if let Some((mod_name, name)) = import {
157 write!(
158 f,
159 "function signature mismatch in imported function '{}' of module '{}'. ",
160 name, mod_name
161 )?;
162 } else {
163 f.write_str("cannot invoke function due to mismatch of function signature. ")?;
164 }
165 write!(
166 f,
167 "expected '[{}] -> [{}]' but got '[{}] -> [{}]'",
168 JoinWritable(expected_params, " "),
169 JoinWritable(expected_results, " "),
170 JoinWritable(actual_params, " "),
171 JoinWritable(actual_results, " "),
172 )?
173 }
174 LoadMemoryOutOfRange {
175 max,
176 addr,
177 operation,
178 ty,
179 } => write!(
180 f,
181 "cannot {} {} value at 0x{:x} due to out of range of memory. memory size is 0x{:x}",
182 operation, ty, addr, max,
183 )?,
184 ImportFuncCallFail { mod_name, name, msg } => write!(
185 f,
186 "calling imported function '{}' in module '{}': {}",
187 name, mod_name, msg,
188 )?,
189 WrongInvokeTarget { name, actual: None } => write!(f, "cannot invoke unknown function '{}'", name)?,
190 WrongInvokeTarget {
191 name,
192 actual: Some(actual),
193 } => write!(
194 f,
195 "cannot invoke '{name}': '{name}' is {actual}",
196 name = name,
197 actual = actual,
198 )?,
199 InvokeInvalidArgs { name, args, arg_types } => write!(
200 f,
201 "cannot invoke function '{}' since given values [{}] does not match to parameter types [{}]",
202 name,
203 JoinWritable(args, ", "),
204 JoinWritable(arg_types, " "),
205 )?,
206 RemZeroDivisor => f.write_str("attempt to calculate reminder with zero divisor")?,
207 DivByZeroOrOverflow => f.write_str("integer overflow or attempt to divide integer by zero")?,
208 ValueOutOfRange { src_val, dest_type } => write!(
209 f,
210 "source value '{}' cannot represent destination type '{}'",
211 src_val, dest_type
212 )?,
213 }
214 write!(f, ": execution was trapped at byte offset 0x{:x}", self.offset)
215 }
216}
217
218pub type Result<T> = ::std::result::Result<T, Box<Trap>>;