libchisel/
lib.rs

1pub use parity_wasm::elements::Module;
2
3use std::{error, fmt};
4
5pub mod imports;
6
7#[cfg(feature = "binaryen")]
8pub mod binaryenopt;
9pub mod checkfloat;
10pub mod checkstartfunc;
11pub mod deployer;
12pub mod dropsection;
13pub mod remapimports;
14pub mod remapstart;
15pub mod repack;
16pub mod snip;
17pub mod trimexports;
18pub mod trimstartfunc;
19pub mod verifyexports;
20pub mod verifyimports;
21
22mod depgraph;
23
24#[derive(Eq, PartialEq, Debug)]
25pub enum ModuleKind {
26    Creator,
27    Translator,
28    Validator,
29}
30
31#[derive(Eq, PartialEq, Debug, Clone)]
32pub enum ModuleError {
33    NotSupported,
34    NotFound,
35    Custom(String),
36}
37
38/// Utility interface for chisel modules.
39pub trait ChiselModule<'a> {
40    type ObjectReference: ?Sized;
41    /// Returns the name of the chisel module.
42    fn id(&'a self) -> String;
43
44    fn kind(&'a self) -> ModuleKind;
45
46    /// Borrows the instance as a trait object.
47    fn as_abstract(&'a self) -> Self::ObjectReference;
48}
49
50pub trait ModuleCreator {
51    /// Returns new module.
52    fn create(&self) -> Result<Module, ModuleError>;
53}
54
55pub trait ModuleTranslator {
56    /// Translates module. Returns new module or none if nothing was modified. Can fail with ModuleError::NotSupported.
57    fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError>;
58
59    /// Translates module in-place. Returns true if the module was modified. Can fail with ModuleError::NotSupported.
60    fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError>;
61}
62
63pub trait ModuleValidator {
64    /// Validates module. Returns true if it is valid or false if invalid.
65    fn validate(&self, module: &Module) -> Result<bool, ModuleError>;
66}
67
68pub trait ModulePreset {
69    fn with_preset(preset: &str) -> Result<Self, ModuleError>
70    where
71        Self: std::marker::Sized;
72}
73
74impl From<String> for ModuleError {
75    fn from(error: String) -> Self {
76        ModuleError::Custom(error)
77    }
78}
79
80impl From<std::io::Error> for ModuleError {
81    fn from(error: std::io::Error) -> Self {
82        use std::error::Error;
83        ModuleError::Custom(error.description().to_string())
84    }
85}
86
87// Also aliased as parity_wasm::SerializationError
88impl From<parity_wasm::elements::Error> for ModuleError {
89    fn from(a: parity_wasm::elements::Error) -> Self {
90        use std::error::Error;
91        ModuleError::Custom(a.description().to_string())
92    }
93}
94
95impl fmt::Display for ModuleError {
96    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97        write!(
98            f,
99            "{}",
100            match self {
101                ModuleError::NotSupported => "Method unsupported",
102                ModuleError::NotFound => "Not found",
103                ModuleError::Custom(msg) => msg,
104            }
105        )
106    }
107}
108
109impl error::Error for ModuleError {
110    fn description(&self) -> &str {
111        match self {
112            ModuleError::NotSupported => "Method unsupported",
113            ModuleError::NotFound => "Not found",
114            ModuleError::Custom(msg) => msg,
115        }
116    }
117
118    fn cause(&self) -> Option<&dyn error::Error> {
119        None
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use std::error::Error;
126
127    use super::*;
128
129    struct SampleModule {}
130
131    impl ModuleCreator for SampleModule {
132        fn create(&self) -> Result<Module, ModuleError> {
133            Ok(Module::default())
134        }
135    }
136
137    impl ModuleTranslator for SampleModule {
138        fn translate(&self, _module: &Module) -> Result<Option<Module>, ModuleError> {
139            Ok(Some(Module::default()))
140        }
141        fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
142            Ok(true)
143        }
144    }
145
146    impl ModuleValidator for SampleModule {
147        fn validate(&self, _module: &Module) -> Result<bool, ModuleError> {
148            Ok(true)
149        }
150    }
151
152    impl<'a> ChiselModule<'a> for SampleModule {
153        // Yes, it implements all the traits, but we will treat it as a validator when used as a
154        // trait object for testing purposes.
155        type ObjectReference = &'a dyn ModuleValidator;
156
157        fn id(&'a self) -> String {
158            "Sample".to_string()
159        }
160
161        fn kind(&'a self) -> ModuleKind {
162            ModuleKind::Validator
163        }
164
165        fn as_abstract(&'a self) -> Self::ObjectReference {
166            self as Self::ObjectReference
167        }
168    }
169
170    #[test]
171    fn creator_succeeds() {
172        let creator = SampleModule {};
173        let result = creator.create();
174        assert!(result.is_ok());
175    }
176
177    #[test]
178    fn translator_succeeds() {
179        let translator = SampleModule {};
180        let result = translator.translate(&Module::default());
181        assert!(result.is_ok());
182    }
183
184    #[test]
185    fn translator_inplace_succeeds() {
186        let translator = SampleModule {};
187        let result = translator.translate_inplace(&mut Module::default());
188        assert!(result.is_ok());
189    }
190
191    #[test]
192    fn validator_succeeds() {
193        let validator = SampleModule {};
194        let result = validator.validate(&Module::default());
195        assert!(result.is_ok());
196    }
197
198    #[test]
199    fn from_error() {
200        let err: ModuleError = "custom message".to_string().into();
201        assert_eq!(err, ModuleError::Custom("custom message".to_string()));
202    }
203
204    #[test]
205    fn fmt_good() {
206        // Add new tests for each enum variant here as they are implemented.
207        let fmt_result_unsupported = format!("{}", ModuleError::NotSupported);
208        assert_eq!("Method unsupported", fmt_result_unsupported);
209
210        let fmt_result_custom = format!("{}", ModuleError::Custom("foo".to_string()));
211        assert_eq!("foo", fmt_result_custom);
212    }
213
214    #[test]
215    fn error_good() {
216        // Add new tests for each enum variant here as they are implemented.
217        let err_unsupported = ModuleError::NotSupported;
218        let err_description_unsupported = err_unsupported.description();
219        assert_eq!("Method unsupported", err_description_unsupported);
220
221        let err_custom = ModuleError::Custom("bar".to_string());
222        let err_description_custom = err_custom.description();
223        assert_eq!("bar", err_description_custom);
224    }
225
226    #[test]
227    fn opaque_module() {
228        let validator = SampleModule {};
229        assert_eq!(validator.id(), "Sample");
230
231        let opaque: &dyn ChiselModule<ObjectReference = &dyn ModuleValidator> =
232            &validator as &dyn ChiselModule<ObjectReference = &dyn ModuleValidator>;
233
234        assert_eq!(opaque.kind(), ModuleKind::Validator);
235
236        let as_trait: &dyn ModuleValidator = opaque.as_abstract();
237
238        let result = as_trait.validate(&Module::default());
239        assert!(result.is_ok());
240    }
241}