wabt/
lib.rs

1//! Bindings to the [wabt](https://github.com/WebAssembly/wabt) library.
2//!
3
4#![deny(missing_docs)]
5
6extern crate serde;
7extern crate serde_json;
8extern crate wabt_sys;
9#[macro_use]
10extern crate serde_derive;
11
12use std::collections::HashMap;
13use std::error;
14use std::ffi::{CStr, CString, NulError};
15use std::fmt;
16use std::os::raw::{c_int, c_void};
17use std::ptr;
18use std::slice;
19
20use wabt_sys as ffi;
21
22pub mod script;
23
24/// A structure to represent errors coming out from wabt.
25#[derive(Debug, PartialEq, Eq)]
26pub struct Error(ErrorKind);
27
28impl Error {
29    /// Returns the `ErrorKind` for this Error.
30    pub fn kind(&self) -> &ErrorKind {
31        &self.0
32    }
33}
34
35impl fmt::Display for Error {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        // TODO: A better formatting
38        write!(f, "error: {:?}", self)
39    }
40}
41
42impl error::Error for Error {
43    fn description(&self) -> &str {
44        match self.0 {
45            ErrorKind::Nul => "string contained nul-byte",
46            ErrorKind::Deserialize(_) => "failed to deserialize",
47            ErrorKind::Parse(_) => "failed to parse",
48            ErrorKind::WriteText => "failed to write text",
49            ErrorKind::NonUtf8Result => "result is not a valid utf8",
50            ErrorKind::WriteBinary => "failed to write binary",
51            ErrorKind::ResolveNames(_) => "failed to resolve names",
52            ErrorKind::Validate(_) => "failed to validate",
53        }
54    }
55}
56
57/// ErrorKind describes an error condition from a wasm module operation, as well as the
58/// corresponding error message from `wabt`, if any.
59#[derive(Debug, PartialEq, Eq)]
60pub enum ErrorKind {
61    /// Result contained an unexpected null byte.
62    Nul,
63    /// Error deserializing binary wasm.
64    Deserialize(String),
65    /// Error parsing textual wasm.
66    Parse(String),
67    /// Error serializing a wasm module to text.
68    WriteText,
69    /// Translating a wasm binary module to text yielded non-utf8 characters.
70    NonUtf8Result,
71    /// Error serializing a wasm module to binary.
72    WriteBinary,
73    /// Error resolving names in the wasm module.
74    ResolveNames(String),
75    /// Error validating the wasm module.
76    Validate(String),
77}
78
79impl From<NulError> for Error {
80    fn from(_e: NulError) -> Error {
81        Error(ErrorKind::Nul)
82    }
83}
84
85struct Lexer {
86    _filename: CString,
87    _buffer: Vec<u8>,
88    raw_lexer: *mut ffi::WastLexer,
89}
90
91impl Lexer {
92    fn new(filename: &str, buffer: &[u8]) -> Result<Lexer, Error> {
93        // TODO: Don't copy.
94        let filename = CString::new(filename)?;
95        let buffer = buffer.to_owned();
96        let lexer = unsafe {
97            ffi::wabt_new_wast_buffer_lexer(
98                filename.as_ptr(),
99                buffer.as_ptr() as *const c_void,
100                buffer.len(),
101            )
102        };
103
104        Ok(Lexer {
105            _filename: filename,
106            _buffer: buffer,
107            raw_lexer: lexer,
108        })
109    }
110}
111
112impl Drop for Lexer {
113    fn drop(&mut self) {
114        unsafe {
115            ffi::wabt_destroy_wast_lexer(self.raw_lexer);
116        }
117    }
118}
119
120struct Errors {
121    raw: *mut ffi::Errors,
122}
123
124impl Errors {
125    fn new() -> Errors {
126        Errors {
127            raw: unsafe { ffi::wabt_new_errors() },
128        }
129    }
130
131    fn format_text(&self, lexer: &Lexer) -> WabtBuf {
132        unsafe {
133            let raw_buffer = ffi::wabt_format_text_errors(self.raw, lexer.raw_lexer);
134            WabtBuf { raw_buffer }
135        }
136    }
137
138    fn format_binary(&self) -> WabtBuf {
139        unsafe {
140            let raw_buffer = ffi::wabt_format_binary_errors(self.raw);
141            WabtBuf { raw_buffer }
142        }
143    }
144}
145
146impl Drop for Errors {
147    fn drop(&mut self) {
148        unsafe { ffi::wabt_destroy_errors(self.raw) }
149    }
150}
151
152/// Represents which WebAssembly features are enabled in Wabt.
153pub struct Features {
154    raw: *mut ffi::Features,
155}
156
157impl Clone for Features {
158    fn clone(&self) -> Self {
159        let mut new = Features::new();
160        new.set_exceptions_enabled(self.exceptions_enabled());
161        new.set_mutable_globals_enabled(self.mutable_globals_enabled());
162        new.set_sat_float_to_int_enabled(self.sat_float_to_int_enabled());
163        new.set_sign_extension_enabled(self.sign_extension_enabled());
164        new.set_simd_enabled(self.simd_enabled());
165        new.set_threads_enabled(self.threads_enabled());
166        new.set_multi_value_enabled(self.multi_value_enabled());
167        new.set_tail_call_enabled(self.tail_call_enabled());
168        new.set_bulk_memory_enabled(self.bulk_memory_enabled());
169        new.set_reference_types_enabled(self.reference_types_enabled());
170        new.set_annotations_enabled(self.annotations_enabled());
171        new
172    }
173}
174
175impl Features {
176    #![allow(missing_docs)]
177    pub fn new() -> Features {
178        let raw = unsafe { ffi::wabt_new_features() };
179        Features { raw }
180    }
181
182    pub fn enable_all(&mut self) {
183        self.enable_exceptions();
184        self.enable_mutable_globals();
185        self.enable_sat_float_to_int();
186        self.enable_sign_extension();
187        self.enable_simd();
188        self.enable_threads();
189        self.enable_multi_value();
190        self.enable_tail_call();
191        self.enable_bulk_memory();
192        self.enable_reference_types();
193        self.enable_annotations();
194    }
195
196    pub fn exceptions_enabled(&self) -> bool {
197        unsafe { ffi::wabt_exceptions_enabled(self.raw) }
198    }
199    pub fn enable_exceptions(&mut self) {
200        self.set_exceptions_enabled(true);
201    }
202    pub fn disable_exceptions(&mut self) {
203        self.set_exceptions_enabled(false);
204    }
205    pub fn set_exceptions_enabled(&mut self, value: bool) {
206        unsafe {
207            ffi::wabt_set_exceptions_enabled(self.raw, value.into());
208        }
209    }
210
211    pub fn mutable_globals_enabled(&self) -> bool {
212        unsafe { ffi::wabt_mutable_globals_enabled(self.raw) }
213    }
214    pub fn enable_mutable_globals(&mut self) {
215        self.set_mutable_globals_enabled(true);
216    }
217    pub fn disable_mutable_globals(&mut self) {
218        self.set_mutable_globals_enabled(false);
219    }
220    pub fn set_mutable_globals_enabled(&mut self, value: bool) {
221        unsafe {
222            ffi::wabt_set_mutable_globals_enabled(self.raw, value.into());
223        }
224    }
225
226    pub fn sat_float_to_int_enabled(&self) -> bool {
227        unsafe { ffi::wabt_sat_float_to_int_enabled(self.raw) }
228    }
229    pub fn enable_sat_float_to_int(&mut self) {
230        self.set_sat_float_to_int_enabled(true);
231    }
232    pub fn disable_sat_float_to_int(&mut self) {
233        self.set_sat_float_to_int_enabled(false);
234    }
235    pub fn set_sat_float_to_int_enabled(&mut self, value: bool) {
236        unsafe {
237            ffi::wabt_set_sat_float_to_int_enabled(self.raw, value.into());
238        }
239    }
240
241    pub fn sign_extension_enabled(&self) -> bool {
242        unsafe { ffi::wabt_sign_extension_enabled(self.raw) }
243    }
244    pub fn enable_sign_extension(&mut self) {
245        self.set_sign_extension_enabled(true);
246    }
247    pub fn disable_sign_extension(&mut self) {
248        self.set_sign_extension_enabled(false);
249    }
250    pub fn set_sign_extension_enabled(&mut self, value: bool) {
251        unsafe {
252            ffi::wabt_set_sign_extension_enabled(self.raw, value.into());
253        }
254    }
255
256    pub fn simd_enabled(&self) -> bool {
257        unsafe { ffi::wabt_simd_enabled(self.raw) }
258    }
259    pub fn enable_simd(&mut self) {
260        self.set_simd_enabled(true);
261    }
262    pub fn disable_simd(&mut self) {
263        self.set_simd_enabled(false);
264    }
265    pub fn set_simd_enabled(&mut self, value: bool) {
266        unsafe {
267            ffi::wabt_set_simd_enabled(self.raw, value.into());
268        }
269    }
270
271    pub fn threads_enabled(&self) -> bool {
272        unsafe { ffi::wabt_threads_enabled(self.raw) }
273    }
274    pub fn enable_threads(&mut self) {
275        self.set_threads_enabled(true);
276    }
277    pub fn disable_threads(&mut self) {
278        self.set_threads_enabled(false);
279    }
280    pub fn set_threads_enabled(&mut self, value: bool) {
281        unsafe {
282            ffi::wabt_set_threads_enabled(self.raw, value.into());
283        }
284    }
285
286    pub fn multi_value_enabled(&self) -> bool {
287        unsafe { ffi::wabt_multi_value_enabled(self.raw) }
288    }
289    pub fn enable_multi_value(&mut self) {
290        self.set_multi_value_enabled(true);
291    }
292    pub fn disable_multi_value(&mut self) {
293        self.set_multi_value_enabled(false);
294    }
295    pub fn set_multi_value_enabled(&mut self, value: bool) {
296        unsafe {
297            ffi::wabt_set_multi_value_enabled(self.raw, value.into());
298        }
299    }
300
301    pub fn tail_call_enabled(&self) -> bool {
302        unsafe { ffi::wabt_tail_call_enabled(self.raw) }
303    }
304    pub fn enable_tail_call(&mut self) {
305        self.set_tail_call_enabled(true);
306    }
307    pub fn disable_tail_call(&mut self) {
308        self.set_tail_call_enabled(false);
309    }
310    pub fn set_tail_call_enabled(&mut self, value: bool) {
311        unsafe {
312            ffi::wabt_set_tail_call_enabled(self.raw, value.into());
313        }
314    }
315
316    pub fn bulk_memory_enabled(&self) -> bool {
317        unsafe { ffi::wabt_bulk_memory_enabled(self.raw) }
318    }
319    pub fn enable_bulk_memory(&mut self) {
320        self.set_bulk_memory_enabled(true);
321    }
322    pub fn disable_bulk_memory(&mut self) {
323        self.set_bulk_memory_enabled(false);
324    }
325    pub fn set_bulk_memory_enabled(&mut self, value: bool) {
326        unsafe {
327            ffi::wabt_set_bulk_memory_enabled(self.raw, value.into());
328        }
329    }
330
331    pub fn reference_types_enabled(&self) -> bool {
332        unsafe { ffi::wabt_reference_types_enabled(self.raw) }
333    }
334    pub fn enable_reference_types(&mut self) {
335        self.set_reference_types_enabled(true);
336    }
337    pub fn disable_reference_types(&mut self) {
338        self.set_reference_types_enabled(false);
339    }
340    pub fn set_reference_types_enabled(&mut self, value: bool) {
341        unsafe {
342            ffi::wabt_set_reference_types_enabled(self.raw, value.into());
343        }
344    }
345
346    pub fn annotations_enabled(&self) -> bool {
347        unsafe { ffi::wabt_annotations_enabled(self.raw) }
348    }
349    pub fn enable_annotations(&mut self) {
350        self.set_annotations_enabled(true);
351    }
352    pub fn disable_annotations(&mut self) {
353        self.set_annotations_enabled(false);
354    }
355    pub fn set_annotations_enabled(&mut self, value: bool) {
356        unsafe {
357            ffi::wabt_set_annotations_enabled(self.raw, value.into());
358        }
359    }
360}
361
362impl Drop for Features {
363    fn drop(&mut self) {
364        unsafe { ffi::wabt_destroy_features(self.raw) }
365    }
366}
367
368struct ParseWatResult {
369    raw_result: *mut ffi::WabtParseWatResult,
370}
371
372impl ParseWatResult {
373    fn is_ok(&self) -> bool {
374        unsafe { ffi::wabt_parse_wat_result_get_result(self.raw_result) == ffi::Result::Ok }
375    }
376
377    fn take_module(self) -> Result<*mut ffi::WasmModule, ()> {
378        if self.is_ok() {
379            unsafe { Ok(ffi::wabt_parse_wat_result_release_module(self.raw_result)) }
380        } else {
381            Err(())
382        }
383    }
384}
385
386impl Drop for ParseWatResult {
387    fn drop(&mut self) {
388        unsafe {
389            ffi::wabt_destroy_parse_wat_result(self.raw_result);
390        }
391    }
392}
393
394fn parse_wat(lexer: &Lexer, features: &Features, errors: &Errors) -> ParseWatResult {
395    let raw_result = unsafe { ffi::wabt_parse_wat(lexer.raw_lexer, features.raw, errors.raw) };
396    ParseWatResult { raw_result }
397}
398
399struct ReadBinaryResult {
400    raw_result: *mut ffi::WabtReadBinaryResult,
401}
402
403impl ReadBinaryResult {
404    fn is_ok(&self) -> bool {
405        unsafe { ffi::wabt_read_binary_result_get_result(self.raw_result) == ffi::Result::Ok }
406    }
407
408    fn take_module(self) -> Result<*mut ffi::WasmModule, ()> {
409        if self.is_ok() {
410            unsafe { Ok(ffi::wabt_read_binary_result_release_module(self.raw_result)) }
411        } else {
412            Err(())
413        }
414    }
415}
416
417impl Drop for ReadBinaryResult {
418    fn drop(&mut self) {
419        unsafe {
420            ffi::wabt_destroy_read_binary_result(self.raw_result);
421        }
422    }
423}
424
425/// Buffer returned by wabt.
426///
427/// # Examples
428///
429/// You can convert it either to `Vec`:
430///
431/// ```rust
432/// # extern crate wabt;
433/// # let wabt_buf = wabt::Wat2Wasm::new().convert("(module)").unwrap();
434/// let vec: Vec<u8> = wabt_buf.as_ref().to_vec();
435/// ```
436///
437/// Or in `String`:
438///
439/// ```rust
440/// # extern crate wabt;
441/// # let wabt_buf = wabt::Wat2Wasm::new().convert("(module)").unwrap();
442/// let text = String::from_utf8(wabt_buf.as_ref().to_vec()).unwrap();
443/// ```
444///
445pub struct WabtBuf {
446    raw_buffer: *mut ffi::OutputBuffer,
447}
448
449impl AsRef<[u8]> for WabtBuf {
450    fn as_ref(&self) -> &[u8] {
451        unsafe {
452            let size = ffi::wabt_output_buffer_get_size(self.raw_buffer);
453            if size == 0 {
454                return &[];
455            }
456
457            let data = ffi::wabt_output_buffer_get_data(self.raw_buffer) as *const u8;
458
459            slice::from_raw_parts(data, size)
460        }
461    }
462}
463
464impl Drop for WabtBuf {
465    fn drop(&mut self) {
466        unsafe {
467            ffi::wabt_destroy_output_buffer(self.raw_buffer);
468        }
469    }
470}
471
472struct WriteModuleResult {
473    raw_result: *mut ffi::WabtWriteModuleResult,
474}
475
476impl WriteModuleResult {
477    fn is_ok(&self) -> bool {
478        unsafe { ffi::wabt_write_module_result_get_result(self.raw_result) == ffi::Result::Ok }
479    }
480
481    fn take_wabt_buf(self) -> Result<WabtBuf, ()> {
482        if self.is_ok() {
483            let raw_buffer =
484                unsafe { ffi::wabt_write_module_result_release_output_buffer(self.raw_result) };
485            Ok(WabtBuf { raw_buffer })
486        } else {
487            Err(())
488        }
489    }
490}
491
492impl Drop for WriteModuleResult {
493    fn drop(&mut self) {
494        unsafe { ffi::wabt_destroy_write_module_result(self.raw_result) }
495    }
496}
497
498struct WriteBinaryOptions {
499    log: bool,
500    canonicalize_lebs: bool,
501    relocatable: bool,
502    write_debug_names: bool,
503}
504
505impl Default for WriteBinaryOptions {
506    fn default() -> WriteBinaryOptions {
507        WriteBinaryOptions {
508            log: false,
509            canonicalize_lebs: true,
510            relocatable: false,
511            write_debug_names: false,
512        }
513    }
514}
515
516struct WriteTextOptions {
517    fold_exprs: bool,
518    inline_export: bool,
519}
520
521impl Default for WriteTextOptions {
522    fn default() -> WriteTextOptions {
523        WriteTextOptions {
524            fold_exprs: false,
525            inline_export: false,
526        }
527    }
528}
529
530/// Options for reading read binary.
531pub struct ReadBinaryOptions {
532    features: Features,
533    read_debug_names: bool,
534}
535
536impl Default for ReadBinaryOptions {
537    fn default() -> ReadBinaryOptions {
538        ReadBinaryOptions {
539            features: Features::new(),
540            read_debug_names: false,
541        }
542    }
543}
544
545struct ParseWastResult {
546    raw_result: *mut ffi::WabtParseWastResult,
547}
548
549impl ParseWastResult {
550    fn is_ok(&self) -> bool {
551        unsafe { ffi::wabt_parse_wast_result_get_result(self.raw_result) == ffi::Result::Ok }
552    }
553
554    fn take_script(self) -> Result<*mut ffi::Script, ()> {
555        if self.is_ok() {
556            unsafe { Ok(ffi::wabt_parse_wast_result_release_module(self.raw_result)) }
557        } else {
558            Err(())
559        }
560    }
561}
562
563impl Drop for ParseWastResult {
564    fn drop(&mut self) {
565        unsafe {
566            ffi::wabt_destroy_parse_wast_result(self.raw_result);
567        }
568    }
569}
570
571fn parse_wast(lexer: &Lexer, features: &Features, errors: &Errors) -> ParseWastResult {
572    let raw_result = unsafe { ffi::wabt_parse_wast(lexer.raw_lexer, features.raw, errors.raw) };
573    ParseWastResult { raw_result }
574}
575
576struct Script {
577    raw_script: *mut ffi::Script,
578    lexer: Lexer,
579    features: Features,
580}
581
582impl Script {
583    fn parse<S: AsRef<[u8]>>(
584        filename: &str,
585        source: S,
586        features: Features,
587    ) -> Result<Script, Error> {
588        let lexer = Lexer::new(filename, source.as_ref())?;
589        let errors = Errors::new();
590        match parse_wast(&lexer, &features, &errors).take_script() {
591            Ok(raw_script) => Ok(Script {
592                raw_script,
593                features,
594                lexer,
595            }),
596            Err(()) => {
597                let msg = String::from_utf8_lossy(errors.format_text(&lexer).as_ref()).to_string();
598                Err(Error(ErrorKind::Parse(msg)))
599            }
600        }
601    }
602
603    fn resolve_names(&self) -> Result<(), Error> {
604        let errors = Errors::new();
605        unsafe {
606            let result = ffi::wabt_resolve_names_script(self.raw_script, errors.raw);
607            if result == ffi::Result::Error {
608                let msg =
609                    String::from_utf8_lossy(errors.format_text(&self.lexer).as_ref()).to_string();
610                return Err(Error(ErrorKind::ResolveNames(msg)));
611            }
612        }
613        Ok(())
614    }
615
616    fn validate(&self) -> Result<(), Error> {
617        let errors = Errors::new();
618        unsafe {
619            let result = ffi::wabt_validate_script(self.raw_script, self.features.raw, errors.raw);
620            if result == ffi::Result::Error {
621                let msg =
622                    String::from_utf8_lossy(errors.format_text(&self.lexer).as_ref()).to_string();
623                return Err(Error(ErrorKind::Validate(msg)));
624            }
625        }
626        Ok(())
627    }
628
629    fn write_binaries(&self, source: &str) -> Result<WabtWriteScriptResult, Error> {
630        let source_cstr = CString::new(source)?;
631
632        unsafe {
633            let raw_script_result = ffi::wabt_write_binary_spec_script(
634                self.raw_script,
635                source_cstr.as_ptr(),
636                ptr::null(),
637                0,
638                1,
639                0,
640                0,
641            );
642            Ok(WabtWriteScriptResult { raw_script_result })
643        }
644    }
645}
646
647/// WebAssembly module.
648pub struct Module {
649    raw_module: *mut ffi::WasmModule,
650    lexer: Option<Lexer>,
651    features: Features,
652}
653
654impl Module {
655    /// Parse source in WebAssembly text format.
656    pub fn parse_wat<S: AsRef<[u8]>>(
657        filename: &str,
658        source: S,
659        features: Features,
660    ) -> Result<Module, Error> {
661        let lexer = Lexer::new(filename, source.as_ref())?;
662        let errors = Errors::new();
663        match parse_wat(&lexer, &features, &errors).take_module() {
664            Ok(module) => Ok(Module {
665                raw_module: module,
666                features,
667                lexer: Some(lexer),
668            }),
669            Err(()) => {
670                let msg = String::from_utf8_lossy(errors.format_text(&lexer).as_ref()).to_string();
671                Err(Error(ErrorKind::Parse(msg)))
672            }
673        }
674    }
675
676    /// Read WebAssembly binary.
677    ///
678    /// `read_binary` doesn't do any validation. If you want to validate, you can the module you can
679    /// call [`validate`].
680    ///
681    /// [`validate`]: #method.validate
682    pub fn read_binary<S: AsRef<[u8]>>(
683        wasm: S,
684        options: &ReadBinaryOptions,
685    ) -> Result<Module, Error> {
686        let errors = Errors::new();
687        let result = {
688            let wasm = wasm.as_ref();
689            let raw_result = unsafe {
690                ffi::wabt_read_binary(
691                    wasm.as_ptr(),
692                    wasm.len(),
693                    options.read_debug_names as c_int,
694                    options.features.raw,
695                    errors.raw,
696                )
697            };
698            ReadBinaryResult { raw_result }
699        };
700        match result.take_module() {
701            Ok(module) => Ok(Module {
702                raw_module: module,
703                features: options.features.clone(),
704                lexer: None,
705            }),
706            Err(()) => {
707                let msg = String::from_utf8_lossy(errors.format_binary().as_ref()).to_string();
708                Err(Error(ErrorKind::Deserialize(msg)))
709            }
710        }
711    }
712
713    fn resolve_names(&mut self) -> Result<(), Error> {
714        let errors = Errors::new();
715        unsafe {
716            let result = ffi::wabt_resolve_names_module(self.raw_module, errors.raw);
717            if result == ffi::Result::Error {
718                let buf = if let Some(ref lexer) = self.lexer {
719                    errors.format_text(lexer)
720                } else {
721                    errors.format_binary()
722                };
723                let msg = String::from_utf8_lossy(buf.as_ref()).to_string();
724                return Err(Error(ErrorKind::ResolveNames(msg)));
725            }
726        }
727        Ok(())
728    }
729
730    /// Validate the module.
731    pub fn validate(&self) -> Result<(), Error> {
732        let errors = Errors::new();
733        unsafe {
734            let result = ffi::wabt_validate_module(self.raw_module, self.features.raw, errors.raw);
735            if result == ffi::Result::Error {
736                let buf = if let Some(ref lexer) = self.lexer {
737                    errors.format_text(lexer)
738                } else {
739                    errors.format_binary()
740                };
741                let msg = String::from_utf8_lossy(buf.as_ref()).to_string();
742                return Err(Error(ErrorKind::Validate(msg)));
743            }
744        }
745        Ok(())
746    }
747
748    fn write_binary(&self, options: &WriteBinaryOptions) -> Result<WabtBuf, Error> {
749        let result = unsafe {
750            let raw_result = ffi::wabt_write_binary_module(
751                self.raw_module,
752                options.log as c_int,
753                options.canonicalize_lebs as c_int,
754                options.relocatable as c_int,
755                options.write_debug_names as c_int,
756            );
757            WriteModuleResult { raw_result }
758        };
759        result
760            .take_wabt_buf()
761            .map_err(|_| Error(ErrorKind::WriteBinary))
762    }
763
764    fn write_text(&self, options: &WriteTextOptions) -> Result<WabtBuf, Error> {
765        let result = unsafe {
766            let raw_result = ffi::wabt_write_text_module(
767                self.raw_module,
768                options.fold_exprs as c_int,
769                options.inline_export as c_int,
770            );
771            WriteModuleResult { raw_result }
772        };
773        result
774            .take_wabt_buf()
775            .map_err(|_| Error(ErrorKind::WriteText))
776    }
777}
778
779impl Drop for Module {
780    fn drop(&mut self) {
781        unsafe {
782            ffi::wabt_destroy_module(self.raw_module);
783        }
784    }
785}
786
787/// A builder for translating wasm text source to wasm binary format.
788///
789/// This version allows you to tweak parameters. If you need simple version
790/// check out [`wat2wasm`].
791///
792/// [`wat2wasm`]: fn.wat2wasm.html
793///
794/// # Examples
795///
796/// ```rust
797/// extern crate wabt;
798/// use wabt::Wat2Wasm;
799///
800/// fn main() {
801///     let wasm_binary = Wat2Wasm::new()
802///         .canonicalize_lebs(false)
803///         .write_debug_names(true)
804///         .convert(
805///             r#"
806///                 (module
807///                     (import "spectest" "print" (func $print (param i32)))
808///                     (func (export "main")
809///                         i32.const 1312
810///                         call $print
811///                     )
812///                 )
813///             "#
814///         ).unwrap();
815///
816///     # wasm_binary;
817/// }
818/// ```
819///
820pub struct Wat2Wasm {
821    validate: bool,
822    write_binary_options: WriteBinaryOptions,
823    features: Features,
824}
825
826impl Wat2Wasm {
827    /// Create `Wat2Wasm` with default configuration.
828    pub fn new() -> Wat2Wasm {
829        Wat2Wasm {
830            write_binary_options: WriteBinaryOptions::default(),
831            validate: true,
832            features: Features::new(),
833        }
834    }
835
836    /// Write canonicalized LEB128 for var ints.
837    ///
838    /// Set this to `false` to write all LEB128 sizes as 5-bytes instead of their minimal size.
839    /// `true` by default.
840    pub fn canonicalize_lebs(&mut self, canonicalize_lebs: bool) -> &mut Wat2Wasm {
841        self.write_binary_options.canonicalize_lebs = canonicalize_lebs;
842        self
843    }
844
845    /// Create a relocatable wasm binary
846    ///
847    /// (suitable for linking with wasm-link).
848    /// `false` by default.
849    pub fn relocatable(&mut self, relocatable: bool) -> &mut Wat2Wasm {
850        self.write_binary_options.relocatable = relocatable;
851        self
852    }
853
854    /// Write debug names to the generated binary file
855    ///
856    /// `false` by default.
857    pub fn write_debug_names(&mut self, write_debug_names: bool) -> &mut Wat2Wasm {
858        self.write_binary_options.write_debug_names = write_debug_names;
859        self
860    }
861
862    /// Check for validity of module before writing.
863    ///
864    /// `true` by default.
865    pub fn validate(&mut self, validate: bool) -> &mut Wat2Wasm {
866        self.validate = validate;
867        self
868    }
869
870    // TODO: Add logged version of convert
871
872    /// Perform conversion.
873    pub fn convert<S: AsRef<[u8]>>(&self, source: S) -> Result<WabtBuf, Error> {
874        let mut module = Module::parse_wat("test.wast", source, self.features.clone())?;
875        module.resolve_names()?;
876
877        if self.validate {
878            module.validate()?;
879        }
880
881        let result = module.write_binary(&self.write_binary_options)?;
882        Ok(result)
883    }
884}
885
886/// A builder for converting wasm binary to wasm text format.
887///
888/// # Examples
889///
890/// ```rust
891/// extern crate wabt;
892/// use wabt::Wasm2Wat;
893///
894/// fn main() {
895///     let wasm_text = Wasm2Wat::new()
896///         .fold_exprs(true)
897///         .inline_export(true)
898///         .convert(
899///             &[
900///                 0, 97, 115, 109, // \0ASM - magic
901///                 1, 0, 0, 0       //  0x01 - version
902///             ]
903///         ).unwrap();
904///
905///     # wasm_text;
906/// }
907/// ```
908///
909pub struct Wasm2Wat {
910    read_binary_options: ReadBinaryOptions,
911    write_text_options: WriteTextOptions,
912}
913
914impl Wasm2Wat {
915    /// Create `Wasm2Wat` with default configuration.
916    pub fn new() -> Wasm2Wat {
917        Wasm2Wat {
918            read_binary_options: ReadBinaryOptions::default(),
919            write_text_options: WriteTextOptions::default(),
920        }
921    }
922
923    /// Support for pre-standard features.
924    pub fn features(&mut self, features: Features) -> &mut Wasm2Wat {
925        self.read_binary_options.features = features;
926        self
927    }
928
929    /// Read debug names in the binary file.
930    ///
931    /// `false` by default.
932    pub fn read_debug_names(&mut self, read_debug_names: bool) -> &mut Wasm2Wat {
933        self.read_binary_options.read_debug_names = read_debug_names;
934        self
935    }
936
937    /// Write folded expressions where possible.
938    ///
939    /// Example of folded code (if `true`):
940    ///
941    /// ```WebAssembly
942    /// (module
943    ///     (func (param i32 i32) (result i32)
944    ///         (i32.add ;; Add loaded arguments
945    ///             (get_local 0) ;; Load first arg
946    ///             (get_local 1) ;; Load second arg
947    ///         )
948    ///     )
949    /// )
950    /// ```
951    ///
952    /// Example of straight code (if `false`):
953    ///
954    /// ```WebAssembly
955    /// (module
956    ///     (func (param i32 i32) (result i32)
957    ///         get_local 0 ;; Load first arg
958    ///         get_local 1 ;; Load second arg
959    ///         i32.add     ;; Add loaded arguments
960    ///     )
961    /// )
962    /// ```
963    ///
964    /// `false` by default.
965    pub fn fold_exprs(&mut self, fold_exprs: bool) -> &mut Wasm2Wat {
966        self.write_text_options.fold_exprs = fold_exprs;
967        self
968    }
969
970    /// Write all exports inline.
971    ///
972    /// Example of code with inline exports (if `true`):
973    ///
974    /// ```WebAssembly
975    /// (module
976    /// (func $addTwo (export "addTwo") (param $p0 i32) (param $p1 i32) (result i32)
977    ///   (i32.add
978    ///     (get_local $p0)
979    ///     (get_local $p1))))
980    /// ```
981    ///
982    /// Example of code with separate exports (if `false`):
983    ///
984    /// ```WebAssembly
985    /// (module
986    ///   (func $addTwo (param $p0 i32) (param $p1 i32) (result i32)
987    ///     (i32.add
988    ///       (get_local $p0)
989    ///       (get_local $p1)))
990    ///   (export "addTwo" (func $addTwo)))
991    /// ```
992    ///
993    /// `false` by default.
994    pub fn inline_export(&mut self, inline_export: bool) -> &mut Wasm2Wat {
995        self.write_text_options.inline_export = inline_export;
996        self
997    }
998
999    /// Perform conversion.
1000    pub fn convert<S: AsRef<[u8]>>(&self, wasm: S) -> Result<WabtBuf, Error> {
1001        let module = Module::read_binary(wasm, &self.read_binary_options)?;
1002        let output_buffer = module.write_text(&self.write_text_options)?;
1003        Ok(output_buffer)
1004    }
1005}
1006
1007/// Translate wasm text source to wasm binary format.
1008///
1009/// If wasm source is valid wasm binary will be returned in the vector.
1010/// Returned binary is validated and can be executed.
1011///
1012/// This function will make translation with default parameters.
1013/// If you want to find out what default parameters are or you want to tweak them
1014/// you can use [`Wat2Wasm`]
1015///
1016/// For more examples and online demo you can check online version
1017/// of [wat2wasm](https://cdn.rawgit.com/WebAssembly/wabt/aae5a4b7/demo/wat2wasm/).
1018///
1019/// [`Wat2Wasm`]: struct.Wat2Wasm.html
1020///
1021/// # Examples
1022///
1023/// ```rust
1024/// extern crate wabt;
1025/// use wabt::wat2wasm;
1026///
1027/// fn main() {
1028///     assert_eq!(
1029///         wat2wasm("(module)").unwrap(),
1030///         &[
1031///             0, 97, 115, 109, // \0ASM - magic
1032///             1, 0, 0, 0       //  0x01 - version
1033///         ]
1034///     );
1035/// }
1036/// ```
1037///
1038pub fn wat2wasm<S: AsRef<[u8]>>(source: S) -> Result<Vec<u8>, Error> {
1039    let result_buf = Wat2Wasm::new().convert(source)?;
1040    Ok(result_buf.as_ref().to_vec())
1041}
1042
1043/// Translate wasm text source to wasm binary format.
1044///
1045/// If wasm source is valid wasm binary will be returned in the vector.
1046/// Returned binary is validated and can be executed.
1047///
1048/// This function will make translation with custom features.
1049/// If you want to find out what default parameters are or you want to tweak them
1050/// you can use [`Wat2Wasm`]
1051///
1052/// For more examples and online demo you can check online version
1053/// of [wat2wasm](https://cdn.rawgit.com/WebAssembly/wabt/aae5a4b7/demo/wat2wasm/).
1054///
1055/// [`Wat2Wasm`]: struct.Wat2Wasm.html
1056///
1057/// # Examples
1058///
1059/// ```rust
1060/// extern crate wabt;
1061/// use wabt::{Features, wat2wasm_with_features};
1062///
1063/// fn main() {
1064///     let mut features = Features::new();
1065///     features.enable_simd();
1066///     assert_eq!(
1067///         wat2wasm_with_features("(module)", features).unwrap(),
1068///         &[
1069///             0, 97, 115, 109, // \0ASM - magic
1070///             1, 0, 0, 0       //  0x01 - version
1071///         ]
1072///     );
1073/// }
1074/// ```
1075///
1076pub fn wat2wasm_with_features<S: AsRef<[u8]>>(
1077    source: S,
1078    features: Features,
1079) -> Result<Vec<u8>, Error> {
1080    let mut wat2wasm = Wat2Wasm::new();
1081    wat2wasm.features = features;
1082    let result_buf = wat2wasm.convert(source)?;
1083    Ok(result_buf.as_ref().to_vec())
1084}
1085
1086/// Disassemble wasm binary to wasm text format.
1087///
1088/// # Examples
1089///
1090/// ```rust
1091/// extern crate wabt;
1092/// use wabt::wasm2wat;
1093///
1094/// fn main() {
1095///     assert_eq!(
1096///         wasm2wat(&[
1097///             0, 97, 115, 109, // \0ASM - magic
1098///             1, 0, 0, 0       //    01 - version
1099///         ]),
1100///         Ok("(module)\n".to_owned()),
1101///     );
1102/// }
1103/// ```
1104///
1105pub fn wasm2wat<S: AsRef<[u8]>>(wasm: S) -> Result<String, Error> {
1106    wasm2wat_with_features(wasm, Features::new())
1107}
1108
1109/// Disassemble wasm binary to wasm text format with the given features.
1110///
1111/// # Examples
1112///
1113/// ```rust
1114/// extern crate wabt;
1115/// use wabt::{Features, wasm2wat_with_features};
1116///
1117/// fn main() {
1118///     let mut features = Features::new();
1119///     features.enable_simd();
1120///     assert_eq!(
1121///         wasm2wat_with_features(&[
1122///             0, 97, 115, 109, // \0ASM - magic
1123///             1, 0, 0, 0       //    01 - version
1124///         ], features),
1125///         Ok("(module)\n".to_owned()),
1126///     );
1127/// }
1128/// ```
1129///
1130pub fn wasm2wat_with_features<S: AsRef<[u8]>>(
1131    wasm: S,
1132    features: Features,
1133) -> Result<String, Error> {
1134    let result_buf = Wasm2Wat::new().features(features).convert(wasm)?;
1135    let text = String::from_utf8(result_buf.as_ref().to_vec())
1136        .map_err(|_| Error(ErrorKind::NonUtf8Result))?;
1137    Ok(text)
1138}
1139
1140struct WabtWriteScriptResult {
1141    raw_script_result: *mut ffi::WabtWriteScriptResult,
1142}
1143
1144struct WabtWriteScriptResultRelease {
1145    json_output_buffer: WabtBuf,
1146    _log_output_buffer: WabtBuf,
1147    module_output_buffers: HashMap<CString, WabtBuf>,
1148}
1149
1150impl WabtWriteScriptResult {
1151    fn is_ok(&self) -> bool {
1152        unsafe {
1153            ffi::wabt_write_script_result_get_result(self.raw_script_result) == ffi::Result::Ok
1154        }
1155    }
1156
1157    fn module_count(&self) -> usize {
1158        unsafe { ffi::wabt_write_script_result_get_module_count(self.raw_script_result) }
1159    }
1160
1161    fn module_filename(&self, index: usize) -> &CStr {
1162        assert!(index < self.module_count());
1163        unsafe {
1164            let s =
1165                ffi::wabt_write_script_result_get_module_filename(self.raw_script_result, index);
1166            CStr::from_ptr(s)
1167        }
1168    }
1169
1170    fn take_all(self) -> Result<WabtWriteScriptResultRelease, ()> {
1171        if self.is_ok() {
1172            let json_output_buffer;
1173            let log_output_buffer;
1174            let mut module_output_buffers = HashMap::new();
1175            unsafe {
1176                json_output_buffer = ffi::wabt_write_script_result_release_json_output_buffer(
1177                    self.raw_script_result,
1178                );
1179                log_output_buffer =
1180                    ffi::wabt_write_script_result_release_log_output_buffer(self.raw_script_result);
1181            }
1182            for i in 0..self.module_count() {
1183                let module_output_buffer = unsafe {
1184                    ffi::wabt_write_script_result_release_module_output_buffer(
1185                        self.raw_script_result,
1186                        i,
1187                    )
1188                };
1189                let name = self.module_filename(i);
1190                module_output_buffers.insert(
1191                    name.to_owned(),
1192                    WabtBuf {
1193                        raw_buffer: module_output_buffer,
1194                    },
1195                );
1196            }
1197            Ok(WabtWriteScriptResultRelease {
1198                json_output_buffer: WabtBuf {
1199                    raw_buffer: json_output_buffer,
1200                },
1201                _log_output_buffer: WabtBuf {
1202                    raw_buffer: log_output_buffer,
1203                },
1204                module_output_buffers,
1205            })
1206        } else {
1207            Err(())
1208        }
1209    }
1210}
1211
1212impl Drop for WabtWriteScriptResult {
1213    fn drop(&mut self) {
1214        unsafe {
1215            ffi::wabt_destroy_write_script_result(self.raw_script_result);
1216        }
1217    }
1218}
1219
1220#[test]
1221fn features() {
1222    let example_wat = r#"
1223(module
1224  (func $simd (result v128)
1225    (v128.const i8x16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)
1226    return)
1227)"#;
1228
1229    assert!(wat2wasm(example_wat).is_err());
1230
1231    let mut features = Features::new();
1232    features.enable_simd();
1233    assert!(wat2wasm_with_features(example_wat, features).is_ok());
1234}
1235
1236#[test]
1237fn module() {
1238    let binary_module = wat2wasm(
1239        r#"
1240(module
1241  (import "foo" "bar" (func (param f32)))
1242  (memory (data "hi"))
1243  (type (func (param i32) (result i32)))
1244  (start 1)
1245  (table 0 1 anyfunc)
1246  (func)
1247  (func (type 1)
1248    i32.const 42
1249    drop)
1250  (export "e" (func 1)))
1251"#,
1252    )
1253    .unwrap();
1254
1255    let mut module = Module::read_binary(&binary_module, &ReadBinaryOptions::default()).unwrap();
1256    module.resolve_names().unwrap();
1257    module.validate().unwrap();
1258}
1259
1260#[test]
1261fn test_wat2wasm() {
1262    assert_eq!(
1263        wat2wasm("(module)").unwrap(),
1264        &[0, 97, 115, 109, 1, 0, 0, 0]
1265    );
1266
1267    assert_eq!(
1268        wat2wasm(
1269            r#"
1270            (module
1271            )"#
1272        )
1273        .unwrap(),
1274        &[0, 97, 115, 109, 1, 0, 0, 0]
1275    );
1276
1277    assert_eq!(
1278        wat2wasm("(modu"),
1279        Err(Error(ErrorKind::Parse(
1280            r#"test.wast:1:2: error: unexpected token "modu", expected a module field or a module.
1281(modu
1282 ^^^^
1283"#
1284            .to_string()
1285        )))
1286    );
1287}
1288
1289#[test]
1290fn test_wasm2wat() {
1291    assert_eq!(
1292        wasm2wat(&[
1293            0, 97, 115, 109, // \0ASM - magic
1294            1, 0, 0, 0 //    01 - version
1295        ]),
1296        Ok("(module)\n".to_owned()),
1297    );
1298
1299    assert_eq!(
1300        wasm2wat(&[
1301            0, 97, 115, 109, // \0ASM - magic
1302        ]),
1303        Err(Error(ErrorKind::Deserialize(
1304            "0000004: error: unable to read uint32_t: version\n".to_owned()
1305        ))),
1306    );
1307}
1308
1309#[test]
1310#[cfg_attr(rustfmt, rustfmt_skip)]
1311fn roundtrip() {
1312    #[cfg_attr(rustfmt, rustfmt_skip)]
1313    let factorial: &[u8] = &[
1314        0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 124, 1, 124, 3, 2, 1, 0, 7, 7,
1315        1, 3, 102, 97, 99, 0, 0, 10, 46, 1, 44, 0, 32, 0, 68, 0, 0, 0, 0, 0, 0, 240,
1316        63, 99, 4, 124, 68, 0, 0, 0, 0, 0, 0, 240, 63, 5, 32, 0, 32, 0, 68, 0, 0, 0,
1317        0, 0, 0, 240, 63, 161, 16, 0, 162, 11, 11
1318    ];
1319
1320    let text = wasm2wat(&factorial).unwrap();
1321    let binary = wat2wasm(&text).unwrap();
1322
1323    assert_eq!(&*factorial, &*binary);
1324}