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