1mod moduletype;
2mod types;
3
4use std::path::Path;
5use std::rc::Rc;
6use thiserror::Error;
7use wasmparser;
8use witx::{self, Id, Module};
9
10pub use self::moduletype::ModuleType;
11pub use self::types::{FuncSignature, ImportFunc};
12pub use witx::{AtomType, Document, WitxError};
13
14#[derive(Debug, Error)]
15pub enum Error {
16 #[error("WebAssembly validation error at offset {1}: {0}")]
17 WasmValidation(&'static str, usize),
18 #[error("Unsupported: {0}")]
19 Unsupported(String),
20 #[error("Module not found: {0}")]
21 ModuleNotFound(String),
22 #[error("Import not found: {module}::{field}")]
23 ImportNotFound { module: String, field: String },
24 #[error("Export not found: {field}")]
25 ExportNotFound { field: String },
26 #[error("Import type error: for {module}::{field}, expected {expected:?}, got {got:?}")]
27 ImportTypeError {
28 module: String,
29 field: String,
30 expected: FuncSignature,
31 got: FuncSignature,
32 },
33 #[error("Export type error: for {field}, expected {expected:?}, got {got:?}")]
34 ExportTypeError {
35 field: String,
36 expected: FuncSignature,
37 got: FuncSignature,
38 },
39}
40
41impl From<wasmparser::BinaryReaderError> for Error {
42 fn from(e: wasmparser::BinaryReaderError) -> Error {
43 Error::WasmValidation(e.message, e.offset)
44 }
45}
46
47pub struct Validator {
48 witx: Document,
49 wasi_exe: bool,
50}
51
52impl Validator {
53 pub fn new(witx: Document, wasi_exe: bool) -> Self {
54 Self { witx, wasi_exe }
55 }
56
57 pub fn parse(source: &str) -> Result<Self, WitxError> {
58 let witx = witx::parse(source)?;
59 Ok(Self {
60 witx,
61 wasi_exe: false,
62 })
63 }
64
65 pub fn load<P: AsRef<Path>>(source_path: P) -> Result<Self, WitxError> {
66 let witx = witx::load(&[source_path.as_ref()])?;
67 Ok(Self {
68 witx,
69 wasi_exe: false,
70 })
71 }
72
73 pub fn wasi_exe(&mut self, check: bool) {
74 self.wasi_exe = check;
75 }
76
77 pub fn with_wasi_exe(mut self, check: bool) -> Self {
78 self.wasi_exe(check);
79 self
80 }
81
82 pub fn validate(&self, module_contents: &[u8]) -> Result<(), Error> {
83 wasmparser::validate(module_contents, None)?;
84
85 let moduletype = ModuleType::parse_wasm(module_contents)?;
86
87 for import in moduletype.imports() {
88 let func = self
89 .witx_module(&import.module)?
90 .func(&Id::new(&import.field))
91 .ok_or_else(|| Error::ImportNotFound {
92 module: import.module.clone(),
93 field: import.field.clone(),
94 })?;
95 let spec_type = FuncSignature::from(func.core_type());
96 if spec_type != import.ty {
97 Err(Error::ImportTypeError {
98 module: import.module,
99 field: import.field,
100 got: import.ty,
101 expected: spec_type,
102 })?;
103 }
104 }
105
106 if self.wasi_exe {
107 self.check_wasi_start_func(&moduletype)?;
108 }
109
110 Ok(())
111 }
112
113 fn witx_module(&self, module: &str) -> Result<Rc<Module>, Error> {
114 self.witx
115 .module(&Id::new(module))
116 .ok_or_else(|| Error::ModuleNotFound(module.to_string()))
117 }
118
119 fn check_wasi_start_func(&self, moduletype: &ModuleType) -> Result<(), Error> {
120 let start_name = "_start";
121 let expected = FuncSignature {
122 args: vec![],
123 ret: None,
124 };
125 if let Some(startfunc) = moduletype.export(start_name) {
126 if startfunc != &expected {
127 Err(Error::ExportTypeError {
128 field: start_name.to_string(),
129 expected,
130 got: startfunc.clone(),
131 })
132 } else {
133 Ok(())
134 }
135 } else {
136 Err(Error::ExportNotFound {
137 field: start_name.to_string(),
138 })
139 }
140 }
141}