externref/processor/
error.rs

1//! Processing errors.
2
3use std::{error, fmt};
4
5use crate::ReadError;
6
7/// Location of a `Resource`: a function argument or a return type.
8#[derive(Debug)]
9pub enum Location {
10    /// Argument with the specified zero-based index.
11    Arg(usize),
12    /// Return type with the specified zero-based index.
13    ReturnType(usize),
14}
15
16impl fmt::Display for Location {
17    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match self {
19            Self::Arg(idx) => write!(formatter, "arg #{idx}"),
20            Self::ReturnType(idx) => write!(formatter, "return type #{idx}"),
21        }
22    }
23}
24
25/// Errors that can occur when [processing] a WASM module.
26///
27/// [processing]: super::Processor::process()
28#[derive(Debug)]
29#[non_exhaustive]
30pub enum Error {
31    /// Error reading the custom section with function declarations from the module.
32    Read(ReadError),
33    /// Error parsing the WASM module.
34    Wasm(anyhow::Error),
35
36    /// Unexpected type of an import (expected a function).
37    UnexpectedImportType {
38        /// Name of the module.
39        module: String,
40        /// Name of the function.
41        name: String,
42    },
43    /// Missing exported function with the enclosed name.
44    NoExport(String),
45    /// Unexpected type of an export (expected a function).
46    UnexpectedExportType(String),
47    /// Imported or exported function has unexpected arity.
48    UnexpectedArity {
49        /// Name of the module; `None` for exported functions.
50        module: Option<String>,
51        /// Name of the function.
52        name: String,
53        /// Expected arity of the function.
54        expected_arity: usize,
55        /// Actual arity of the function.
56        real_arity: usize,
57    },
58    /// Argument or return type of a function has unexpected type.
59    UnexpectedType {
60        /// Name of the module; `None` for exported functions.
61        module: Option<String>,
62        /// Name of the function.
63        name: String,
64        /// Location of an argument / return type in the function.
65        location: Location,
66        /// Actual type of the function (the expected type is always `i32`).
67        real_type: walrus::ValType,
68    },
69
70    /// Incorrectly placed `externref` guard. This is caused by processing the WASM module
71    /// with external tools (e.g., `wasm-opt`) before using this processor.
72    IncorrectGuard {
73        /// Name of the function with an incorrectly placed guard.
74        function_name: Option<String>,
75        /// WASM bytecode offset of the offending guard.
76        code_offset: Option<u32>,
77    },
78    /// Unexpected call to a function returning `externref`. Such calls should be confined
79    /// in order for the processor to work properly. Like with [`Self::IncorrectGuard`],
80    /// such errors should only be caused by external tools (e.g., `wasm-opt`).
81    UnexpectedCall {
82        /// Name of the function containing an unexpected call.
83        function_name: Option<String>,
84        /// WASM bytecode offset of the offending call.
85        code_offset: Option<u32>,
86    },
87}
88
89impl fmt::Display for Error {
90    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91        const EXTERNAL_TOOL_TIP: &str = "This can be caused by an external WASM manipulation tool \
92            such as `wasm-opt`. Please run such tools *after* the externref processor.";
93
94        match self {
95            Self::Read(err) => write!(formatter, "failed reading WASM custom section: {err}"),
96            Self::Wasm(err) => write!(formatter, "failed reading WASM module: {err}"),
97
98            Self::UnexpectedImportType { module, name } => {
99                write!(
100                    formatter,
101                    "unexpected type of import `{module}::{name}`; expected a function"
102                )
103            }
104
105            Self::NoExport(name) => {
106                write!(formatter, "missing exported function `{name}`")
107            }
108            Self::UnexpectedExportType(name) => {
109                write!(
110                    formatter,
111                    "unexpected type of export `{name}`; expected a function"
112                )
113            }
114
115            Self::UnexpectedArity {
116                module,
117                name,
118                expected_arity,
119                real_arity,
120            } => {
121                let module_descr = module
122                    .as_ref()
123                    .map_or_else(String::new, |module| format!(" imported from `{module}`"));
124                write!(
125                    formatter,
126                    "unexpected arity for function `{name}`{module_descr}: \
127                     expected {expected_arity}, got {real_arity}"
128                )
129            }
130            Self::UnexpectedType {
131                module,
132                name,
133                location,
134                real_type,
135            } => {
136                let module_descr = module
137                    .as_ref()
138                    .map_or_else(String::new, |module| format!(" imported from `{module}`"));
139                write!(
140                    formatter,
141                    "{location} of function `{name}`{module_descr} has unexpected type; \
142                     expected `i32`, got {real_type}"
143                )
144            }
145
146            Self::IncorrectGuard {
147                function_name,
148                code_offset,
149            } => {
150                let function_name = function_name
151                    .as_ref()
152                    .map_or("(unnamed function)", String::as_str);
153                let code_offset = code_offset
154                    .as_ref()
155                    .map_or_else(String::new, |offset| format!(" at {offset}"));
156                write!(
157                    formatter,
158                    "incorrectly placed externref guard in {function_name}{code_offset}. \
159                     {EXTERNAL_TOOL_TIP}"
160                )
161            }
162            Self::UnexpectedCall {
163                function_name,
164                code_offset,
165            } => {
166                let function_name = function_name
167                    .as_ref()
168                    .map_or("(unnamed function)", String::as_str);
169                let code_offset = code_offset
170                    .as_ref()
171                    .map_or_else(String::new, |offset| format!(" at {offset}"));
172                write!(
173                    formatter,
174                    "unexpected call to an `externref`-returning function \
175                     in {function_name}{code_offset}. {EXTERNAL_TOOL_TIP}"
176                )
177            }
178        }
179    }
180}
181
182impl From<ReadError> for Error {
183    fn from(err: ReadError) -> Self {
184        Self::Read(err)
185    }
186}
187
188impl error::Error for Error {
189    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
190        match self {
191            Self::Read(err) => Some(err),
192            Self::Wasm(err) => Some(err.as_ref()),
193            _ => None,
194        }
195    }
196}