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}