Skip to main content

wasmtime/compile/
code_builder.rs

1use crate::Engine;
2use crate::prelude::*;
3use std::borrow::Cow;
4use std::path::Path;
5
6#[cfg(feature = "compile-time-builtins")]
7use crate::hash_map::HashMap;
8
9#[cfg(feature = "compile-time-builtins")]
10mod compile_time_builtins;
11
12/// Builder-style structure used to create a [`Module`](crate::module::Module) or
13/// pre-compile a module to a serialized list of bytes.
14///
15/// This structure can be used for more advanced configuration when compiling a
16/// WebAssembly module. Most configuration can use simpler constructors such as:
17///
18/// * [`Module::new`](crate::Module::new)
19/// * [`Module::from_file`](crate::Module::from_file)
20/// * [`Module::from_binary`](crate::Module::from_binary)
21///
22/// Note that a [`CodeBuilder`] always involves compiling WebAssembly bytes
23/// to machine code. To deserialize a list of bytes use
24/// [`Module::deserialize`](crate::Module::deserialize) instead.
25///
26/// A [`CodeBuilder`] requires a source of WebAssembly bytes to be configured
27/// before calling [`compile_module_serialized`] or [`compile_module`]. This can
28/// be provided with either the [`wasm_binary`] or [`wasm_binary_file`] method.
29/// Note that only a single source of bytes can be provided.
30///
31/// # WebAssembly Text Format
32///
33/// This builder supports the WebAssembly Text Format (`*.wat` files) through
34/// the [`CodeBuilder::wasm_binary_or_text`] and
35/// [`CodeBuilder::wasm_binary_or_text_file`] methods. These methods
36/// automatically convert WebAssembly text files to binary. Note though that
37/// this behavior is disabled if the `wat` crate feature is not enabled.
38///
39/// [`compile_module_serialized`]: CodeBuilder::compile_module_serialized
40/// [`compile_module`]: CodeBuilder::compile_module
41/// [`wasm_binary`]: CodeBuilder::wasm_binary
42/// [`wasm_binary_file`]: CodeBuilder::wasm_binary_file
43pub struct CodeBuilder<'a> {
44    pub(super) engine: &'a Engine,
45    wasm: Option<Cow<'a, [u8]>>,
46    wasm_path: Option<Cow<'a, Path>>,
47    dwarf_package: Option<Cow<'a, [u8]>>,
48    dwarf_package_path: Option<Cow<'a, Path>>,
49    unsafe_intrinsics_import: Option<String>,
50
51    /// A map from import name to the Wasm bytes of the associated compile-time
52    /// builtin and its file path, if any.
53    //
54    // XXX: we can't use `'a` here without forcing us to change a bunch of
55    // callers because `HashMap` has a `Drop` implementation, so `dropck` thinks
56    // that all `'a` borrows could be used by `CodeBuilder`'s `Drop`, which
57    // means that a bunch of the existing calls to `.wasm_bytes` and such would
58    // need to be reordered with the `CodeBuilder`'s construction.
59    #[cfg(feature = "compile-time-builtins")]
60    compile_time_builtins: HashMap<Cow<'a, str>, Cow<'a, [u8]>>,
61}
62
63/// Return value of [`CodeBuilder::hint`]
64pub enum CodeHint {
65    /// Hint that the code being compiled is a module.
66    Module,
67    /// Hint that the code being compiled is a component.
68    Component,
69}
70
71impl<'a> CodeBuilder<'a> {
72    /// Creates a new builder which will insert modules into the specified
73    /// [`Engine`].
74    pub fn new(engine: &'a Engine) -> Self {
75        CodeBuilder {
76            engine,
77            wasm: None,
78            wasm_path: None,
79            dwarf_package: None,
80            dwarf_package_path: None,
81            unsafe_intrinsics_import: None,
82            #[cfg(feature = "compile-time-builtins")]
83            compile_time_builtins: HashMap::default(),
84        }
85    }
86
87    /// Configures the WebAssembly binary that is being compiled.
88    ///
89    /// The `wasm_bytes` parameter must be a binary WebAssembly file.
90    /// This will be stored within the [`CodeBuilder`] for processing later when
91    /// compilation is finalized.
92    ///
93    /// The optional `wasm_path` parameter is the path to the `wasm_bytes` on
94    /// disk, if any. This may be used for diagnostics and other
95    /// debugging-related purposes, but this method will not read the path
96    /// specified.
97    ///
98    /// # Errors
99    ///
100    /// This method will return an error if WebAssembly bytes have already been
101    /// configured.
102    pub fn wasm_binary(
103        &mut self,
104        wasm_bytes: impl Into<Cow<'a, [u8]>>,
105        wasm_path: Option<&'a Path>,
106    ) -> Result<&mut Self> {
107        if self.wasm.is_some() {
108            bail!("cannot configure wasm bytes twice");
109        }
110        self.wasm = Some(wasm_bytes.into());
111        self.wasm_path = wasm_path.map(|p| p.into());
112
113        if self.wasm_path.is_some() {
114            self.dwarf_package_from_wasm_path()?;
115        }
116
117        Ok(self)
118    }
119
120    /// Equivalent of [`CodeBuilder::wasm_binary`] that also accepts the
121    /// WebAssembly text format.
122    ///
123    /// This method will configure the WebAssembly binary to be compiled. The
124    /// input `wasm_bytes` may either be the wasm text format or the binary
125    /// format. If the `wat` crate feature is enabled, which is enabled by
126    /// default, then the text format will automatically be converted to the
127    /// binary format.
128    ///
129    /// # Errors
130    ///
131    /// This method will return an error if WebAssembly bytes have already been
132    /// configured. This method will also return an error if `wasm_bytes` is the
133    /// wasm text format and the text syntax is not valid.
134    pub fn wasm_binary_or_text(
135        &mut self,
136        wasm_bytes: &'a [u8],
137        wasm_path: Option<&'a Path>,
138    ) -> Result<&mut Self> {
139        #[cfg(feature = "wat")]
140        let wasm_bytes = wat::parse_bytes(wasm_bytes).map_err(|mut e| {
141            if let Some(path) = wasm_path {
142                e.set_path(path);
143            }
144            e
145        })?;
146        self.wasm_binary(wasm_bytes, wasm_path)
147    }
148
149    /// Reads the `file` specified for the WebAssembly bytes that are going to
150    /// be compiled.
151    ///
152    /// This method will read `file` from the filesystem and interpret it
153    /// as a WebAssembly binary.
154    ///
155    /// A DWARF package file will be probed using the root of `file` and with a
156    /// `.dwp` extension. If found, it will be loaded and DWARF fusion
157    /// performed.
158    ///
159    /// # Errors
160    ///
161    /// This method will return an error if WebAssembly bytes have already been
162    /// configured.
163    ///
164    /// If `file` can't be read or an error happens reading it then that will
165    /// also be returned.
166    ///
167    /// If DWARF fusion is performed and the DWARF packaged file cannot be read
168    /// then an error will be returned.
169    pub fn wasm_binary_file(&mut self, file: &'a Path) -> Result<&mut Self> {
170        let wasm = std::fs::read(file)
171            .with_context(|| format!("failed to read input file: {}", file.display()))?;
172        self.wasm_binary(wasm, Some(file))
173    }
174
175    /// Equivalent of [`CodeBuilder::wasm_binary_file`] that also accepts the
176    /// WebAssembly text format.
177    ///
178    /// This method is will read the file at `path` and interpret the contents
179    /// to determine if it's the wasm text format or binary format. The file
180    /// extension of `file` is not consulted. The text format is automatically
181    /// converted to the binary format if the crate feature `wat` is active.
182    ///
183    /// # Errors
184    ///
185    /// In addition to the errors returned by [`CodeBuilder::wasm_binary_file`]
186    /// this may also fail if the text format is read and the syntax is invalid.
187    pub fn wasm_binary_or_text_file(&mut self, file: &'a Path) -> Result<&mut Self> {
188        #[cfg(feature = "wat")]
189        {
190            let wasm = wat::parse_file(file)?;
191            self.wasm_binary(wasm, Some(file))
192        }
193        #[cfg(not(feature = "wat"))]
194        {
195            self.wasm_binary_file(file)
196        }
197    }
198
199    /// Get the Wasm to be compiled.
200    ///
201    /// When using compile-time builtins, compose the builtins and the guest
202    /// Wasm together, before returning the Wasm.
203    pub(super) fn get_wasm(&self) -> Result<Cow<'_, [u8]>> {
204        let wasm = self
205            .wasm
206            .as_deref()
207            .ok_or_else(|| format_err!("no wasm bytes have been configured"))?;
208
209        #[cfg(not(feature = "compile-time-builtins"))]
210        {
211            Ok(wasm.into())
212        }
213
214        #[cfg(feature = "compile-time-builtins")]
215        {
216            self.compose_compile_time_builtins(wasm)
217                .context("failed to compose compile-time builtins with the main Wasm")
218        }
219    }
220
221    /// Expose Wasmtime's unsafe intrinsics under the given import name.
222    ///
223    /// These intrinsics provide native memory loads and stores to Wasm; they
224    /// are *extremely* unsafe! If you are not absolutely sure that you need
225    /// these unsafe intrinsics, *do not use them!* See the safety section below
226    /// for details.
227    ///
228    /// This functionality is intended to be used when implementing
229    /// [compile-time builtins][CodeBuilder::compile_time_builtins_binary]; that
230    /// is, satisfying a Wasm import via special-cased, embedder-specific code
231    /// at compile time. You should never use these intrinsics to intentionally
232    /// subvert the Wasm sandbox. You should strive to implement safe functions
233    /// that encapsulate your uses of these intrinsics such that, regardless of
234    /// any value given as arguments, your functions *cannot* result in loading
235    /// from or storing to invalid pointers, or any other kind of unsafety. See
236    /// below for an example of the intended use cases.
237    ///
238    /// Wasmtime's unsafe intrinsics can only be exposed to Wasm components, not
239    /// core modules, currently.
240    ///
241    /// Note that when compile-time builtins are defined on a `CodeBuilder`,
242    /// only the compile-time builtins can import the unsafe intrinsics, and the
243    /// main guest program cannot import them.
244    ///
245    /// # Safety
246    ///
247    /// Extreme care must be taken when using these intrinsics.
248    ///
249    /// All loads of or stores to pointers derived from `store-data-address` are
250    /// inherently tied to a particular `T` type in a `Store<T>`. It is wildly
251    /// unsafe to run a Wasm program that uses unsafe intrinsics to access the
252    /// store's `T` inside a `Store<U>`. You must only run Wasm that uses unsafe
253    /// intrinsics in a `Store<T>` where the `T` is the type expected by the
254    /// Wasm's unsafe-intrinsics usage.
255    ///
256    /// Furthermore, usage of these intrinsics is not only tied to a particular
257    /// `T` type, but also to `T`'s layout on the host platform. The size and
258    /// alignment of `T`, the offsets of its fields, and those fields' size and
259    /// alignment can all vary across not only architecture but also operating
260    /// system. With care, you can define your `T` type such that its layout is
261    /// identical across the platforms that you run Wasm on, allowing you to
262    /// reuse the same Wasm binary and its unsafe-intrinsics usage on all your
263    /// platforms. Failing that, you must only run a Wasm program that uses
264    /// unsafe intrinsics on the host platform that its unsafe-intrinsic usage
265    /// is specialized to. See the portability section and example below for
266    /// more details.
267    ///
268    /// You are *strongly* encouraged to add assertions for the layout
269    /// properties that your unsafe-intrinsic usage's safety relies upon:
270    ///
271    /// ```rust
272    /// /// This type is used as `wasmtime::Store<MyData>` and accessed by Wasm via
273    /// /// unsafe intrinsics.
274    /// #[repr(C, align(8))]
275    /// struct MyData {
276    ///     id: u64,
277    ///     counter: u32,
278    ///     buf: [u8; 4],
279    /// }
280    ///
281    /// // Assert that the layout is what our Wasm's unsafe-intrinsics usage expects.
282    /// static _MY_DATA_LAYOUT_ASSERTIONS: () = {
283    ///     assert!(core::mem::size_of::<MyData>() == 16);
284    ///     assert!(core::mem::align_of::<MyData>() == 8);
285    ///     assert!(core::mem::offset_of!(MyData, id) == 0);
286    ///     assert!(core::mem::offset_of!(MyData, counter) == 8);
287    ///     assert!(core::mem::offset_of!(MyData, buf) == 12);
288    /// };
289    /// ```
290    ///
291    /// Finally, every pointer loaded from or stored to must:
292    ///
293    /// * Be non-null
294    ///
295    /// * Be aligned to the access type's natural alignment (e.g. 8-byte alignment
296    ///   for `u64`, 4-byte alignment for `u32`, etc...)
297    ///
298    /// * Point to a memory block that is valid to read from (for loads) or
299    ///   valid to write to (for stores) under Rust's pointer provenance rules
300    ///
301    /// * Point to a memory block that is at least as large as the access type's
302    ///   natural size (e.g. 1 byte for `u8`, 2 bytes for `u16`, etc...)
303    ///
304    /// * Point to a memory block that is not accessed concurrently by any other
305    ///   threads
306    ///
307    /// Failure to uphold any of these invariants will lead to unsafety,
308    /// undefined behavior, and/or data races.
309    ///
310    /// # Intrinsics
311    ///
312    /// | Name                       | Parameters                  | Results |
313    /// |----------------------------|-----------------------------|---------|
314    /// | `u8-native-load`           | `u64`                       | `u8`    |
315    /// | `u16-native-load`          | `u64`                       | `u16`   |
316    /// | `u32-native-load`          | `u64`                       | `u32`   |
317    /// | `u64-native-load`          | `u64`                       | `u64`   |
318    /// | `u8-native-store`          | `u64`, `u8`                 | -       |
319    /// | `u16-native-store`         | `u64`, `u16`                | -       |
320    /// | `u32-native-store`         | `u64`, `u32`                | -       |
321    /// | `u64-native-store`         | `u64`, `u64`                | -       |
322    /// | `u8-checked-native-load`   | `u64`, `u64`, `u64`         | `u8`    |
323    /// | `u16-checked-native-load`  | `u64`, `u64`, `u64`         | `u16`   |
324    /// | `u32-checked-native-load`  | `u64`, `u64`, `u64`         | `u32`   |
325    /// | `u64-checked-native-load`  | `u64`, `u64`, `u64`         | `u64`   |
326    /// | `u8-checked-native-store`  | `u64`, `u64`, `u64`, `u8`   | -       |
327    /// | `u16-checked-native-store` | `u64`, `u64`, `u64`, `u16`  | -       |
328    /// | `u32-checked-native-store` | `u64`, `u64`, `u64`, `u32`  | -       |
329    /// | `u64-checked-native-store` | `u64`, `u64`, `u64`, `u64`  | -       |
330    /// | `store-data-address`       | -                           | `u64`   |
331    ///
332    /// ## `*-native-load`
333    ///
334    /// These intrinsics perform an unsandboxed, unsynchronized load from native
335    /// memory, using the native endianness.
336    ///
337    /// ## `*-native-store`
338    ///
339    /// These intrinsics perform an unsandboxed, unsynchronized store to native
340    /// memory, using the native endianness.
341    ///
342    /// ## `*-checked-native-load`
343    ///
344    /// These intrinsics perform a bounds-checked version of the corresponding
345    /// `*-native-load` intrinsic. Their parameters are a `base_address`, an
346    /// `offset`, and a `length` (in bytes) describing a region of native
347    /// memory, and they load from `base_address + offset`.
348    ///
349    /// Before performing the load, they check that the access is in bounds. If
350    /// `offset + SIZE > length` -- where `SIZE` is the byte width of the access
351    /// (e.g. `4` for `u32-checked-native-load`) -- or if that addition
352    /// overflows, then the intrinsic raises a trap and the load is *not*
353    /// performed. Otherwise the load is performed exactly as the corresponding
354    /// unchecked `*-native-load` intrinsic would, against the address
355    /// `base_address + offset`.
356    ///
357    /// Note that the bounds check is performed against the given `length`
358    /// argument; it is the caller's responsibility to pass a `length` that
359    /// actually describes a valid region of memory beginning at `base_address`.
360    /// When the access is in bounds, the resulting `base_address + offset`
361    /// access must still uphold all of the safety invariants described above
362    /// for the unchecked intrinsics.
363    ///
364    /// ## `*-checked-native-store`
365    ///
366    /// These intrinsics perform a bounds-checked version of the corresponding
367    /// `*-native-store` intrinsic. Their parameters are a `base_address`, an
368    /// `offset`, a `length` (in bytes), and a `value`. They perform the same
369    /// bounds check as the `*-checked-native-load` intrinsics -- trapping,
370    /// without storing anything, if `offset + SIZE > length` or if that
371    /// addition overflows -- and otherwise store `value` to `base_address +
372    /// offset`.
373    ///
374    /// ## `store-data-address`
375    ///
376    /// This intrinsic function returns the pointer to the embedder's `T` data
377    /// inside a `Store<T>`.
378    ///
379    /// In general, all native load and store intinsics should operate on memory
380    /// addresses that are derived from a call to this intrinsic. If you want to
381    /// expose data for raw memory access by Wasm, put it inside the `T` in your
382    /// `Store<T>` and Wasm's access to that data should derive from this
383    /// intrinsic.
384    ///
385    /// # Portability
386    ///
387    /// Loads and stores are always performed using the architecture's native
388    /// endianness.
389    ///
390    /// Addresses passed to and returned from these intrinsics are always
391    /// 64-bits large. The upper half of the value is simply ignored on 32-bit
392    /// architectures.
393    ///
394    /// With care, you can design your store's `T` type such that accessing it
395    /// via these intrinsics is portable, and you can reuse a single Wasm binary
396    /// (and its set of intrinsic calls) across all of the platforms, with the
397    /// following rules of thumb:
398    ///
399    /// * Only access `u8`, `u16`, `u32`, and `u64` data via these intrinsics.
400    ///
401    /// * If you need to access other types of data, encode it into those types
402    ///   and then access the encoded data from the intrinsics.
403    ///
404    /// * Use `union`s to encode pointers and pointer-sized data as a `u64` and
405    ///   then access it via the `u64-native-{load,store}` intrinsics. See
406    ///   `ExposedPointer` in the example below.
407    ///
408    /// # Example
409    ///
410    /// The following example shows how you can use unsafe intrinsics and
411    /// compile-time builtins to give Wasm direct zero-copy access to a host
412    /// buffer.
413    ///
414    /// ```rust
415    /// use std::mem;
416    /// use wasmtime::*;
417    ///
418    /// // A `*mut u8` pointer that is exposed directly to Wasm via unsafe intrinsics.
419    /// #[repr(align(8))]
420    /// union ExposedPointer {
421    ///     pointer: *mut u8,
422    ///     padding: u64,
423    /// }
424    ///
425    /// static _EXPOSED_POINTER_LAYOUT_ASSERTIONS: () = {
426    ///     assert!(mem::size_of::<ExposedPointer>() == 8);
427    ///     assert!(mem::align_of::<ExposedPointer>() == 8);
428    /// };
429    ///
430    /// impl ExposedPointer {
431    ///     /// Wrap the given pointer into an `ExposedPointer`.
432    ///     fn new(pointer: *mut u8) -> Self {
433    ///         // NB: Zero-initialize to avoid potential footguns with accessing
434    ///         // undefined bytes.
435    ///         let mut p = Self { padding: 0 };
436    ///         p.pointer = pointer;
437    ///         p
438    ///     }
439    ///
440    ///     /// Get the wrapped pointer.
441    ///     fn get(&self) -> *mut u8 {
442    ///         unsafe { self.pointer }
443    ///     }
444    /// }
445    ///
446    /// /// This is the `T` type we will put inside our
447    /// /// `wasmtime::Store<T>`s. It contains a pointer to a heap-allocated buffer
448    /// /// in host memory, which we will give Wasm zero-copy access to via unsafe
449    /// /// intrinsics.
450    /// #[repr(C)]
451    /// struct StoreData {
452    ///     buf_ptr: ExposedPointer,
453    ///     buf_len: u64,
454    /// }
455    ///
456    /// static _STORE_DATA_LAYOUT_ASSERTIONS: () = {
457    ///     assert!(mem::size_of::<StoreData>() == 16);
458    ///     assert!(mem::align_of::<StoreData>() == 8);
459    ///     assert!(mem::offset_of!(StoreData, buf_ptr) == 0);
460    ///     assert!(mem::offset_of!(StoreData, buf_len) == 8);
461    /// };
462    ///
463    /// impl Drop for StoreData {
464    ///     fn drop(&mut self) {
465    ///         let len = usize::try_from(self.buf_len).unwrap();
466    ///         let ptr = std::ptr::slice_from_raw_parts_mut(self.buf_ptr.get(), len);
467    ///         unsafe {
468    ///             let _ = Box::from_raw(ptr);
469    ///         }
470    ///     }
471    /// }
472    ///
473    /// impl StoreData {
474    ///     /// Create a new `StoreData`, allocating an inner buffer containing
475    ///     /// `bytes`.
476    ///     fn new(bytes: impl IntoIterator<Item = u8>) -> Self {
477    ///         let buf: Box<[u8]> = bytes.into_iter().collect();
478    ///         let ptr = Box::into_raw(buf);
479    ///         Self {
480    ///             buf_ptr: ExposedPointer::new(ptr.cast::<u8>()),
481    ///             buf_len: u64::try_from(ptr.len()).unwrap(),
482    ///         }
483    ///     }
484    ///
485    ///     /// Get the inner buffer as a shared slice.
486    ///     fn buf(&self) -> &[u8] {
487    ///         let ptr = self.buf_ptr.get().cast_const();
488    ///         let len = usize::try_from(self.buf_len).unwrap();
489    ///         unsafe {
490    ///             std::slice::from_raw_parts(ptr, len)
491    ///         }
492    ///     }
493    /// }
494    ///
495    /// # fn main() -> Result<()> {
496    /// // Enable function inlining during compilation. If you are using unsafe intrinsics, you
497    /// // almost assuredly want them inlined to avoid function call overheads.
498    /// let mut config = Config::new();
499    /// config.compiler_inlining(wasmtime::Inlining::Yes);
500    ///
501    /// let engine = Engine::new(&config)?;
502    /// let linker = wasmtime::component::Linker::new(&engine);
503    ///
504    /// // Create a new builder for configuring a Wasm compilation.
505    /// let mut builder = CodeBuilder::new(&engine);
506    ///
507    /// // Allow the code we are building to use Wasmtime's unsafe intrinsics.
508    /// //
509    /// // SAFETY: we wrap all usage of the intrinsics in safe APIs and only instantiate the code
510    /// // within a `Store<T>` where `T = StoreData`, as the code expects.
511    /// unsafe {
512    ///     builder.expose_unsafe_intrinsics("unsafe-intrinsics");
513    /// }
514    ///
515    /// // Define the compile-time builtin that encapsulates the
516    /// // intrinsics' unsafety and builds a safe API on top of them.
517    /// unsafe {
518    ///     builder.compile_time_builtins_binary_or_text(
519    ///         "safe-api",
520    ///         r#"
521    ///             (component
522    ///                 (import "unsafe-intrinsics"
523    ///                     (instance $intrinsics
524    ///                         (export "store-data-address" (func (result u64)))
525    ///                         (export "u64-native-load" (func (param "pointer" u64) (result u64)))
526    ///                         (export "u8-native-load" (func (param "pointer" u64) (result u8)))
527    ///                         (export "u8-native-store" (func (param "pointer" u64) (param "value" u8)))
528    ///                     )
529    ///                 )
530    ///
531    ///                 ;; The core Wasm module that implements the safe API.
532    ///                 (core module $safe-api-impl
533    ///                     (import "" "store-data-address" (func $store-data-address (result i64)))
534    ///                     (import "" "u64-native-load" (func $u64-native-load (param i64) (result i64)))
535    ///                     (import "" "u8-native-load" (func $u8-native-load (param i64) (result i32)))
536    ///                     (import "" "u8-native-store" (func $u8-native-store (param i64 i32)))
537    ///
538    ///                     ;; Load the `StoreData::buf_ptr` field
539    ///                     (func $get-buf-ptr (result i64)
540    ///                         (call $u64-native-load (i64.add (call $store-data-address) (i64.const 0)))
541    ///                     )
542    ///
543    ///                     ;; Load the `StoreData::buf_len` field
544    ///                     (func $get-buf-len (result i64)
545    ///                         (call $u64-native-load (i64.add (call $store-data-address) (i64.const 8)))
546    ///                     )
547    ///
548    ///                     ;; Check that `$i` is within `StoreData` buffer's bounds, raising a trap
549    ///                     ;; otherwise.
550    ///                     (func $bounds-check (param $i i64)
551    ///                         (if (i64.lt_u (local.get $i) (call $get-buf-len))
552    ///                             (then (return))
553    ///                             (else (unreachable))
554    ///                         )
555    ///                     )
556    ///
557    ///                     ;; A safe function to get the `i`th byte from `StoreData`'s buffer,
558    ///                     ;; raising a trap on out-of-bounds accesses.
559    ///                     (func (export "get") (param $i i64) (result i32)
560    ///                         (call $bounds-check (local.get $i))
561    ///                         (call $u8-native-load (i64.add (call $get-buf-ptr) (local.get $i)))
562    ///                     )
563    ///
564    ///                     ;; A safe function to set the `i`th byte in `StoreData`'s buffer,
565    ///                     ;; raising a trap on out-of-bounds accesses.
566    ///                     (func (export "set") (param $i i64) (param $value i32)
567    ///                         (call $bounds-check (local.get $i))
568    ///                         (call $u8-native-store (i64.add (call $get-buf-ptr) (local.get $i))
569    ///                                                (local.get $value))
570    ///                     )
571    ///
572    ///                     ;; A safe function to get the length of the `StoreData` buffer.
573    ///                     (func (export "len") (result i64)
574    ///                         (call $get-buf-len)
575    ///                     )
576    ///                 )
577    ///
578    ///                 ;; Lower the imported intrinsics from component functions to core functions.
579    ///                 (core func $store-data-address' (canon lower (func $intrinsics "store-data-address")))
580    ///                 (core func $u64-native-load' (canon lower (func $intrinsics "u64-native-load")))
581    ///                 (core func $u8-native-load' (canon lower (func $intrinsics "u8-native-load")))
582    ///                 (core func $u8-native-store' (canon lower (func $intrinsics "u8-native-store")))
583    ///
584    ///                 ;; Instantiate our safe API implementation, passing in the lowered unsafe
585    ///                 ;; intrinsics as its imports.
586    ///                 (core instance $instance
587    ///                     (instantiate $safe-api-impl
588    ///                         (with "" (instance
589    ///                             (export "store-data-address" (func $store-data-address'))
590    ///                             (export "u64-native-load" (func $u64-native-load'))
591    ///                             (export "u8-native-load" (func $u8-native-load'))
592    ///                             (export "u8-native-store" (func $u8-native-store'))
593    ///                         ))
594    ///                     )
595    ///                 )
596    ///
597    ///                 ;; Lift the safe API's exports from core functions to component functions
598    ///                 ;; and export them.
599    ///                 (func (export "get") (param "i" u64) (result u8)
600    ///                     (canon lift (core func $instance "get"))
601    ///                 )
602    ///                 (func (export "set") (param "i" u64) (param "value" u8)
603    ///                     (canon lift (core func $instance "set"))
604    ///                 )
605    ///                 (func (export "len") (result u64)
606    ///                     (canon lift (core func $instance "len"))
607    ///                 )
608    ///             )
609    ///         "#.as_bytes(),
610    ///         None,
611    ///     )?;
612    /// }
613    ///
614    /// // Provide the guest Wasm that we are compiling, which uses the safe API we
615    /// // implemented as a compile-time builtin.
616    /// builder.wasm_binary_or_text(
617    ///     r#"
618    ///         (component
619    ///             ;; Import the safe API.
620    ///             (import "safe-api"
621    ///                 (instance $safe-api
622    ///                     (export "get" (func (param "i" u64) (result u8)))
623    ///                     (export "set" (func (param "i" u64) (param "value" u8)))
624    ///                     (export "len" (func (result u64)))
625    ///                 )
626    ///             )
627    ///
628    ///             ;; Define this component's core module implementation.
629    ///             (core module $main-impl
630    ///                 (import "" "get" (func $get (param i64) (result i32)))
631    ///                 (import "" "set" (func $set (param i64 i32)))
632    ///                 (import "" "len" (func $len (result i64)))
633    ///
634    ///                 (func (export "main")
635    ///                     (local $i i64)
636    ///                     (local $n i64)
637    ///
638    ///                     (local.set $i (i64.const 0))
639    ///                     (local.set $n (call $len))
640    ///
641    ///                     (loop $loop
642    ///                         ;; When we have iterated over every byte in the
643    ///                         ;; buffer, exit.
644    ///                         (if (i64.ge_u (local.get $i) (local.get $n))
645    ///                             (then (return)))
646    ///
647    ///                         ;; Increment the `i`th byte in the buffer.
648    ///                         (call $set (local.get $i)
649    ///                                    (i32.add (call $get (local.get $i))
650    ///                                             (i32.const 1)))
651    ///
652    ///                         ;; Increment `i` and continue to the next iteration
653    ///                         ;; of the loop.
654    ///                         (local.set $i (i64.add (local.get $i) (i64.const 1)))
655    ///                         (br $loop)
656    ///                     )
657    ///                 )
658    ///             )
659    ///
660    ///             ;; Lower the imported safe APIs from component functions to core functions.
661    ///             (core func $get' (canon lower (func $safe-api "get")))
662    ///             (core func $set' (canon lower (func $safe-api "set")))
663    ///             (core func $len' (canon lower (func $safe-api "len")))
664    ///
665    ///             ;; Instantiate our module, providing the lowered safe APIs as imports.
666    ///             (core instance $instance
667    ///                 (instantiate $main-impl
668    ///                     (with "" (instance
669    ///                         (export "get" (func $get'))
670    ///                         (export "set" (func $set'))
671    ///                         (export "len" (func $len'))
672    ///                     ))
673    ///                 )
674    ///             )
675    ///
676    ///             ;; Lift the implementation's `main` from a core function to a component function
677    ///             ;; and export it!
678    ///             (func (export "main")
679    ///                 (canon lift (core func $instance "main"))
680    ///             )
681    ///         )
682    ///     "#.as_bytes(),
683    ///     None,
684    /// )?;
685    ///
686    /// // Finish the builder and compile the component.
687    /// let component = builder.compile_component()?;
688    ///
689    /// // Create a new `Store<StoreData>`, wrapping a buffer of the given elements.
690    /// let mut store = Store::new(&engine, StoreData::new([0, 10, 20, 30, 40, 50]));
691    ///
692    /// // Instantiate our component into the store.
693    /// let instance = linker.instantiate(&mut store, &component)?;
694    ///
695    /// // Get the instance's exported `main` function and call it.
696    /// instance
697    ///     .get_typed_func::<(), ()>(&mut store, "main")?
698    ///     .call(&mut store, ())?;
699    ///
700    /// // Our `StoreData`'s buffer had each element incremented directly from Wasm!
701    /// assert_eq!(store.data().buf(), &[1, 11, 21, 31, 41, 51]);
702    /// # Ok(())
703    /// # }
704    /// ```
705    pub unsafe fn expose_unsafe_intrinsics(&mut self, import_name: impl Into<String>) -> &mut Self {
706        self.unsafe_intrinsics_import = Some(import_name.into());
707        self
708    }
709
710    /// Explicitly specify DWARF `.dwp` path.
711    ///
712    /// # Errors
713    ///
714    /// This method will return an error if the `.dwp` file has already been set
715    /// through [`CodeBuilder::dwarf_package`] or auto-detection in
716    /// [`CodeBuilder::wasm_binary_file`].
717    ///
718    /// This method will also return an error if `file` cannot be read.
719    pub fn dwarf_package_file(&mut self, file: &Path) -> Result<&mut Self> {
720        if self.dwarf_package.is_some() {
721            bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
722        }
723
724        let dwarf_package = std::fs::read(file)
725            .with_context(|| format!("failed to read dwarf input file: {}", file.display()))?;
726        self.dwarf_package_path = Some(Cow::Owned(file.to_owned()));
727        self.dwarf_package = Some(dwarf_package.into());
728
729        Ok(self)
730    }
731
732    fn dwarf_package_from_wasm_path(&mut self) -> Result<&mut Self> {
733        let dwarf_package_path_buf = self.wasm_path.as_ref().unwrap().with_extension("dwp");
734        if dwarf_package_path_buf.exists() {
735            return self.dwarf_package_file(dwarf_package_path_buf.as_path());
736        }
737
738        Ok(self)
739    }
740
741    /// Gets the DWARF package.
742    pub(super) fn get_dwarf_package(&self) -> Option<&[u8]> {
743        self.dwarf_package.as_deref()
744    }
745
746    /// Set the DWARF package binary.
747    ///
748    /// Initializes `dwarf_package` from `dwp_bytes` in preparation for
749    /// DWARF fusion. Allows the DWARF package to be supplied as a byte array
750    /// when the file probing performed in `wasm_file` is not appropriate.
751    ///
752    /// # Errors
753    ///
754    /// Returns an error if the `*.dwp` file is already set via auto-probing in
755    /// [`CodeBuilder::wasm_binary_file`] or explicitly via
756    /// [`CodeBuilder::dwarf_package_file`].
757    pub fn dwarf_package(&mut self, dwp_bytes: &'a [u8]) -> Result<&mut Self> {
758        if self.dwarf_package.is_some() {
759            bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
760        }
761        self.dwarf_package = Some(dwp_bytes.into());
762        Ok(self)
763    }
764
765    /// Returns a hint, if possible, of what the provided bytes are.
766    ///
767    /// This method can be use to detect what the previously supplied bytes to
768    /// methods such as [`CodeBuilder::wasm_binary_or_text`] are. This will
769    /// return whether a module or a component was found in the provided bytes.
770    ///
771    /// This method will return `None` if wasm bytes have not been configured
772    /// or if the provided bytes don't look like either a component or a
773    /// module.
774    pub fn hint(&self) -> Option<CodeHint> {
775        let wasm = self.wasm.as_ref()?;
776        if wasmparser::Parser::is_component(wasm) {
777            Some(CodeHint::Component)
778        } else if wasmparser::Parser::is_core_wasm(wasm) {
779            Some(CodeHint::Module)
780        } else {
781            None
782        }
783    }
784
785    /// Finishes this compilation and produces a serialized list of bytes.
786    ///
787    /// This method requires that either [`CodeBuilder::wasm_binary`] or
788    /// related methods were invoked prior to indicate what is being compiled.
789    ///
790    /// This method will block the current thread until compilation has
791    /// finished, and when done the serialized artifact will be returned.
792    ///
793    /// Note that this method will never cache compilations, even if the
794    /// `cache` feature is enabled.
795    ///
796    /// # Errors
797    ///
798    /// This can fail if the input wasm module was not valid or if another
799    /// compilation-related error is encountered.
800    pub fn compile_module_serialized(&self) -> Result<Vec<u8>> {
801        ensure!(
802            self.unsafe_intrinsics_import.is_none(),
803            "`CodeBuilder::expose_unsafe_intrinsics` can only be used with components"
804        );
805
806        #[cfg(feature = "compile-time-builtins")]
807        ensure!(
808            self.get_compile_time_builtins().is_empty(),
809            "compile-time builtins can only be used with components"
810        );
811
812        let wasm = self.get_wasm()?;
813        let dwarf_package = self.get_dwarf_package();
814        let (v, _) =
815            super::build_module_artifacts(self.engine, &wasm, dwarf_package.as_deref(), &())?;
816        Ok(v)
817    }
818
819    /// Same as [`CodeBuilder::compile_module_serialized`] except that it
820    /// compiles a serialized [`Component`](crate::component::Component)
821    /// instead of a module.
822    #[cfg(feature = "component-model")]
823    pub fn compile_component_serialized(&self) -> Result<Vec<u8>> {
824        let wasm = self.get_wasm()?;
825        let (v, _) = super::build_component_artifacts(
826            self.engine,
827            &wasm,
828            None,
829            self.get_unsafe_intrinsics_import(),
830            &(),
831        )?;
832        Ok(v)
833    }
834
835    pub(super) fn get_unsafe_intrinsics_import(&self) -> Option<&str> {
836        self.unsafe_intrinsics_import.as_deref()
837    }
838}
839
840/// This is a helper struct used when caching to hash the state of an `Engine`
841/// used for module compilation.
842///
843/// The hash computed for this structure is used to key the global wasmtime
844/// cache and dictates whether artifacts are reused. Consequently the contents
845/// of this hash dictate when artifacts are or aren't re-used.
846pub struct HashedEngineCompileEnv<'a>(pub &'a Engine);
847
848impl std::hash::Hash for HashedEngineCompileEnv<'_> {
849    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
850        // Hash the compiler's state based on its target and configuration.
851        if let Some(compiler) = self.0.compiler() {
852            compiler.triple().hash(hasher);
853            compiler.flags().hash(hasher);
854            compiler.isa_flags().hash(hasher);
855        }
856
857        // Hash configuration state read for compilation
858        let config = self.0.config();
859        self.0.tunables().hash(hasher);
860        self.0.features().hash(hasher);
861        config.wmemcheck.hash(hasher);
862
863        // Catch accidental bugs of reusing across crate versions.
864        config.module_version.hash(hasher);
865    }
866}