1use crate::prelude::*;
25use crate::{Engine, ModuleVersionStrategy, Precompiled};
26use core::str::FromStr;
27use object::endian::Endianness;
28#[cfg(any(feature = "cranelift", feature = "winch"))]
29use object::write::{Object, StandardSegment};
30use object::{read::elf::ElfFile64, FileFlags, Object as _, ObjectSection};
31use serde_derive::{Deserialize, Serialize};
32use wasmtime_environ::obj;
33use wasmtime_environ::{FlagValue, ObjectKind, Tunables};
34
35const VERSION: u8 = 0;
36
37pub fn check_compatible(engine: &Engine, mmap: &[u8], expected: ObjectKind) -> Result<()> {
46 let obj = ElfFile64::<Endianness>::parse(mmap)
60 .map_err(obj::ObjectCrateErrorWrapper)
61 .context("failed to parse precompiled artifact as an ELF")?;
62 let expected_e_flags = match expected {
63 ObjectKind::Module => obj::EF_WASMTIME_MODULE,
64 ObjectKind::Component => obj::EF_WASMTIME_COMPONENT,
65 };
66 match obj.flags() {
67 FileFlags::Elf {
68 os_abi: obj::ELFOSABI_WASMTIME,
69 abi_version: 0,
70 e_flags,
71 } if e_flags == expected_e_flags => {}
72 _ => bail!("incompatible object file format"),
73 }
74
75 let data = obj
76 .section_by_name(obj::ELF_WASM_ENGINE)
77 .ok_or_else(|| anyhow!("failed to find section `{}`", obj::ELF_WASM_ENGINE))?
78 .data()
79 .map_err(obj::ObjectCrateErrorWrapper)?;
80 let (first, data) = data
81 .split_first()
82 .ok_or_else(|| anyhow!("invalid engine section"))?;
83 if *first != VERSION {
84 bail!("mismatched version in engine section");
85 }
86 let (len, data) = data
87 .split_first()
88 .ok_or_else(|| anyhow!("invalid engine section"))?;
89 let len = usize::from(*len);
90 let (version, data) = if data.len() < len + 1 {
91 bail!("engine section too small")
92 } else {
93 data.split_at(len)
94 };
95
96 match &engine.config().module_version {
97 ModuleVersionStrategy::WasmtimeVersion => {
98 let version = core::str::from_utf8(version)?;
99 if version != env!("CARGO_PKG_VERSION") {
100 bail!(
101 "Module was compiled with incompatible Wasmtime version '{}'",
102 version
103 );
104 }
105 }
106 ModuleVersionStrategy::Custom(v) => {
107 let version = core::str::from_utf8(&version)?;
108 if version != v {
109 bail!(
110 "Module was compiled with incompatible version '{}'",
111 version
112 );
113 }
114 }
115 ModuleVersionStrategy::None => { }
116 }
117 postcard::from_bytes::<Metadata<'_>>(data)?.check_compatible(engine)
118}
119
120#[cfg(any(feature = "cranelift", feature = "winch"))]
121pub fn append_compiler_info(engine: &Engine, obj: &mut Object<'_>, metadata: &Metadata<'_>) {
122 let section = obj.add_section(
123 obj.segment_name(StandardSegment::Data).to_vec(),
124 obj::ELF_WASM_ENGINE.as_bytes().to_vec(),
125 object::SectionKind::ReadOnlyData,
126 );
127 let mut data = Vec::new();
128 data.push(VERSION);
129 let version = match &engine.config().module_version {
130 ModuleVersionStrategy::WasmtimeVersion => env!("CARGO_PKG_VERSION"),
131 ModuleVersionStrategy::Custom(c) => c,
132 ModuleVersionStrategy::None => "",
133 };
134 assert!(
136 version.len() < 256,
137 "package version must be less than 256 bytes"
138 );
139 data.push(version.len() as u8);
140 data.extend_from_slice(version.as_bytes());
141 data.extend(postcard::to_allocvec(metadata).unwrap());
142 obj.set_section_data(section, data, 1);
143}
144
145fn detect_precompiled<'data, R: object::ReadRef<'data>>(
146 obj: ElfFile64<'data, Endianness, R>,
147) -> Option<Precompiled> {
148 match obj.flags() {
149 FileFlags::Elf {
150 os_abi: obj::ELFOSABI_WASMTIME,
151 abi_version: 0,
152 e_flags: obj::EF_WASMTIME_MODULE,
153 } => Some(Precompiled::Module),
154 FileFlags::Elf {
155 os_abi: obj::ELFOSABI_WASMTIME,
156 abi_version: 0,
157 e_flags: obj::EF_WASMTIME_COMPONENT,
158 } => Some(Precompiled::Component),
159 _ => None,
160 }
161}
162
163pub fn detect_precompiled_bytes(bytes: &[u8]) -> Option<Precompiled> {
164 detect_precompiled(ElfFile64::parse(bytes).ok()?)
165}
166
167#[cfg(feature = "std")]
168pub fn detect_precompiled_file(path: impl AsRef<std::path::Path>) -> Result<Option<Precompiled>> {
169 let read_cache = object::ReadCache::new(std::fs::File::open(path)?);
170 let obj = ElfFile64::parse(&read_cache)?;
171 Ok(detect_precompiled(obj))
172}
173
174#[derive(Serialize, Deserialize)]
175pub struct Metadata<'a> {
176 target: String,
177 #[serde(borrow)]
178 shared_flags: Vec<(&'a str, FlagValue<'a>)>,
179 #[serde(borrow)]
180 isa_flags: Vec<(&'a str, FlagValue<'a>)>,
181 tunables: Tunables,
182 features: WasmFeatures,
183}
184
185#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
187struct WasmFeatures {
188 reference_types: bool,
189 multi_value: bool,
190 bulk_memory: bool,
191 component_model: bool,
192 simd: bool,
193 tail_call: bool,
194 threads: bool,
195 multi_memory: bool,
196 exceptions: bool,
197 memory64: bool,
198 relaxed_simd: bool,
199 extended_const: bool,
200 function_references: bool,
201 gc: bool,
202 custom_page_sizes: bool,
203 component_model_async: bool,
204 gc_types: bool,
205 wide_arithmetic: bool,
206 stack_switching: bool,
207}
208
209impl Metadata<'_> {
210 #[cfg(any(feature = "cranelift", feature = "winch"))]
211 pub fn new(engine: &Engine) -> Metadata<'static> {
212 let wasmparser::WasmFeaturesInflated {
213 reference_types,
214 multi_value,
215 bulk_memory,
216 component_model,
217 simd,
218 threads,
219 tail_call,
220 multi_memory,
221 exceptions,
222 memory64,
223 relaxed_simd,
224 extended_const,
225 memory_control,
226 function_references,
227 gc,
228 custom_page_sizes,
229 shared_everything_threads,
230 component_model_values,
231 component_model_nested_names,
232 component_model_async,
233 legacy_exceptions,
234 gc_types,
235 stack_switching,
236 wide_arithmetic,
237
238 mutable_global: _,
240 saturating_float_to_int: _,
241 sign_extension: _,
242 floats: _,
243 } = engine.features().inflate();
244
245 assert!(!memory_control);
249 assert!(!component_model_values);
250 assert!(!component_model_nested_names);
251 assert!(!shared_everything_threads);
252 assert!(!legacy_exceptions);
253
254 Metadata {
255 target: engine.compiler().triple().to_string(),
256 shared_flags: engine.compiler().flags(),
257 isa_flags: engine.compiler().isa_flags(),
258 tunables: engine.tunables().clone(),
259 features: WasmFeatures {
260 reference_types,
261 multi_value,
262 bulk_memory,
263 component_model,
264 simd,
265 threads,
266 tail_call,
267 multi_memory,
268 exceptions,
269 memory64,
270 relaxed_simd,
271 extended_const,
272 function_references,
273 gc,
274 custom_page_sizes,
275 component_model_async,
276 gc_types,
277 wide_arithmetic,
278 stack_switching,
279 },
280 }
281 }
282
283 fn check_compatible(mut self, engine: &Engine) -> Result<()> {
284 self.check_triple(engine)?;
285 self.check_shared_flags(engine)?;
286 self.check_isa_flags(engine)?;
287 self.check_tunables(&engine.tunables())?;
288 self.check_features(&engine.features())?;
289 Ok(())
290 }
291
292 fn check_triple(&self, engine: &Engine) -> Result<()> {
293 let engine_target = engine.target();
294 let module_target =
295 target_lexicon::Triple::from_str(&self.target).map_err(|e| anyhow!(e))?;
296
297 if module_target.architecture != engine_target.architecture {
298 bail!(
299 "Module was compiled for architecture '{}'",
300 module_target.architecture
301 );
302 }
303
304 if module_target.operating_system != engine_target.operating_system {
305 bail!(
306 "Module was compiled for operating system '{}'",
307 module_target.operating_system
308 );
309 }
310
311 Ok(())
312 }
313
314 fn check_shared_flags(&mut self, engine: &Engine) -> Result<()> {
315 for (name, val) in self.shared_flags.iter() {
316 engine
317 .check_compatible_with_shared_flag(name, val)
318 .map_err(|s| anyhow::Error::msg(s))
319 .context("compilation settings of module incompatible with native host")?;
320 }
321 Ok(())
322 }
323
324 fn check_isa_flags(&mut self, engine: &Engine) -> Result<()> {
325 for (name, val) in self.isa_flags.iter() {
326 engine
327 .check_compatible_with_isa_flag(name, val)
328 .map_err(|s| anyhow::Error::msg(s))
329 .context("compilation settings of module incompatible with native host")?;
330 }
331 Ok(())
332 }
333
334 fn check_int<T: Eq + core::fmt::Display>(found: T, expected: T, feature: &str) -> Result<()> {
335 if found == expected {
336 return Ok(());
337 }
338
339 bail!(
340 "Module was compiled with a {} of '{}' but '{}' is expected for the host",
341 feature,
342 found,
343 expected
344 );
345 }
346
347 fn check_bool(found: bool, expected: bool, feature: &str) -> Result<()> {
348 if found == expected {
349 return Ok(());
350 }
351
352 bail!(
353 "Module was compiled {} {} but it {} enabled for the host",
354 if found { "with" } else { "without" },
355 feature,
356 if expected { "is" } else { "is not" }
357 );
358 }
359
360 fn check_tunables(&mut self, other: &Tunables) -> Result<()> {
361 let Tunables {
362 collector,
363 memory_reservation,
364 memory_guard_size,
365 generate_native_debuginfo,
366 parse_wasm_debuginfo,
367 consume_fuel,
368 epoch_interruption,
369 memory_may_move,
370 guard_before_linear_memory,
371 table_lazy_init,
372 relaxed_simd_deterministic,
373 winch_callable,
374 signals_based_traps,
375 memory_init_cow,
376 memory_reservation_for_growth: _,
378
379 generate_address_map: _,
384
385 debug_adapter_modules: _,
387 } = self.tunables;
388
389 Self::check_collector(collector, other.collector)?;
390 Self::check_int(
391 memory_reservation,
392 other.memory_reservation,
393 "memory reservation",
394 )?;
395 Self::check_int(
396 memory_guard_size,
397 other.memory_guard_size,
398 "memory guard size",
399 )?;
400 Self::check_bool(
401 generate_native_debuginfo,
402 other.generate_native_debuginfo,
403 "debug information support",
404 )?;
405 Self::check_bool(
406 parse_wasm_debuginfo,
407 other.parse_wasm_debuginfo,
408 "WebAssembly backtrace support",
409 )?;
410 Self::check_bool(consume_fuel, other.consume_fuel, "fuel support")?;
411 Self::check_bool(
412 epoch_interruption,
413 other.epoch_interruption,
414 "epoch interruption",
415 )?;
416 Self::check_bool(memory_may_move, other.memory_may_move, "memory may move")?;
417 Self::check_bool(
418 guard_before_linear_memory,
419 other.guard_before_linear_memory,
420 "guard before linear memory",
421 )?;
422 Self::check_bool(table_lazy_init, other.table_lazy_init, "table lazy init")?;
423 Self::check_bool(
424 relaxed_simd_deterministic,
425 other.relaxed_simd_deterministic,
426 "relaxed simd deterministic semantics",
427 )?;
428 Self::check_bool(
429 winch_callable,
430 other.winch_callable,
431 "Winch calling convention",
432 )?;
433 Self::check_bool(
434 signals_based_traps,
435 other.signals_based_traps,
436 "Signals-based traps",
437 )?;
438 Self::check_bool(
439 memory_init_cow,
440 other.memory_init_cow,
441 "memory initialization with CoW",
442 )?;
443
444 Ok(())
445 }
446
447 fn check_cfg_bool(
448 cfg: bool,
449 cfg_str: &str,
450 found: bool,
451 expected: bool,
452 feature: &str,
453 ) -> Result<()> {
454 if cfg {
455 Self::check_bool(found, expected, feature)
456 } else {
457 assert!(!expected);
458 ensure!(
459 !found,
460 "Module was compiled with {feature} but support in the host \
461 was disabled at compile time because the `{cfg_str}` Cargo \
462 feature was not enabled",
463 );
464 Ok(())
465 }
466 }
467
468 fn check_features(&mut self, other: &wasmparser::WasmFeatures) -> Result<()> {
469 let WasmFeatures {
470 reference_types,
471 multi_value,
472 bulk_memory,
473 component_model,
474 simd,
475 tail_call,
476 threads,
477 multi_memory,
478 exceptions,
479 memory64,
480 relaxed_simd,
481 extended_const,
482 function_references,
483 gc,
484 custom_page_sizes,
485 component_model_async,
486 gc_types,
487 wide_arithmetic,
488 stack_switching,
489 } = self.features;
490
491 use wasmparser::WasmFeatures as F;
492 Self::check_bool(
493 reference_types,
494 other.contains(F::REFERENCE_TYPES),
495 "WebAssembly reference types support",
496 )?;
497 Self::check_bool(
498 function_references,
499 other.contains(F::FUNCTION_REFERENCES),
500 "WebAssembly function-references support",
501 )?;
502 Self::check_bool(
503 gc,
504 other.contains(F::GC),
505 "WebAssembly garbage collection support",
506 )?;
507 Self::check_bool(
508 multi_value,
509 other.contains(F::MULTI_VALUE),
510 "WebAssembly multi-value support",
511 )?;
512 Self::check_bool(
513 bulk_memory,
514 other.contains(F::BULK_MEMORY),
515 "WebAssembly bulk memory support",
516 )?;
517 Self::check_bool(
518 component_model,
519 other.contains(F::COMPONENT_MODEL),
520 "WebAssembly component model support",
521 )?;
522 Self::check_bool(simd, other.contains(F::SIMD), "WebAssembly SIMD support")?;
523 Self::check_bool(
524 tail_call,
525 other.contains(F::TAIL_CALL),
526 "WebAssembly tail calls support",
527 )?;
528 Self::check_bool(
529 threads,
530 other.contains(F::THREADS),
531 "WebAssembly threads support",
532 )?;
533 Self::check_bool(
534 multi_memory,
535 other.contains(F::MULTI_MEMORY),
536 "WebAssembly multi-memory support",
537 )?;
538 Self::check_bool(
539 exceptions,
540 other.contains(F::EXCEPTIONS),
541 "WebAssembly exceptions support",
542 )?;
543 Self::check_bool(
544 memory64,
545 other.contains(F::MEMORY64),
546 "WebAssembly 64-bit memory support",
547 )?;
548 Self::check_bool(
549 extended_const,
550 other.contains(F::EXTENDED_CONST),
551 "WebAssembly extended-const support",
552 )?;
553 Self::check_bool(
554 relaxed_simd,
555 other.contains(F::RELAXED_SIMD),
556 "WebAssembly relaxed-simd support",
557 )?;
558 Self::check_bool(
559 custom_page_sizes,
560 other.contains(F::CUSTOM_PAGE_SIZES),
561 "WebAssembly custom-page-sizes support",
562 )?;
563 Self::check_bool(
564 component_model_async,
565 other.contains(F::COMPONENT_MODEL_ASYNC),
566 "WebAssembly component model support for async lifts/lowers, futures, streams, and errors",
567 )?;
568 Self::check_cfg_bool(
569 cfg!(feature = "gc"),
570 "gc",
571 gc_types,
572 other.contains(F::GC_TYPES),
573 "support for WebAssembly gc types",
574 )?;
575 Self::check_bool(
576 wide_arithmetic,
577 other.contains(F::WIDE_ARITHMETIC),
578 "WebAssembly wide-arithmetic support",
579 )?;
580 Self::check_bool(
581 stack_switching,
582 other.contains(F::STACK_SWITCHING),
583 "WebAssembly stack switching support",
584 )?;
585 Ok(())
586 }
587
588 fn check_collector(
589 module: Option<wasmtime_environ::Collector>,
590 host: Option<wasmtime_environ::Collector>,
591 ) -> Result<()> {
592 match (module, host) {
593 (None, None) => Ok(()),
594 (Some(module), Some(host)) if module == host => Ok(()),
595
596 (None, Some(_)) => {
597 bail!("module was compiled without GC but GC is enabled in the host")
598 }
599 (Some(_), None) => {
600 bail!("module was compiled with GC however GC is disabled in the host")
601 }
602
603 (Some(module), Some(host)) => {
604 bail!(
605 "module was compiled for the {module} collector but \
606 the host is configured to use the {host} collector",
607 )
608 }
609 }
610 }
611}
612
613#[cfg(test)]
614mod test {
615 use super::*;
616 use crate::{Config, Module, OptLevel};
617 use std::{
618 collections::hash_map::DefaultHasher,
619 hash::{Hash, Hasher},
620 };
621 use tempfile::TempDir;
622
623 #[test]
624 fn test_architecture_mismatch() -> Result<()> {
625 let engine = Engine::default();
626 let mut metadata = Metadata::new(&engine);
627 metadata.target = "unknown-generic-linux".to_string();
628
629 match metadata.check_compatible(&engine) {
630 Ok(_) => unreachable!(),
631 Err(e) => assert_eq!(
632 e.to_string(),
633 "Module was compiled for architecture 'unknown'",
634 ),
635 }
636
637 Ok(())
638 }
639
640 #[test]
641 #[cfg(target_arch = "x86_64")] fn test_os_mismatch() -> Result<()> {
644 let engine = Engine::default();
645 let mut metadata = Metadata::new(&engine);
646
647 metadata.target = format!(
648 "{}-generic-unknown",
649 target_lexicon::Triple::host().architecture
650 );
651
652 match metadata.check_compatible(&engine) {
653 Ok(_) => unreachable!(),
654 Err(e) => assert_eq!(
655 e.to_string(),
656 "Module was compiled for operating system 'unknown'",
657 ),
658 }
659
660 Ok(())
661 }
662
663 #[test]
664 fn test_cranelift_flags_mismatch() -> Result<()> {
665 let engine = Engine::default();
666 let mut metadata = Metadata::new(&engine);
667
668 metadata
669 .shared_flags
670 .push(("preserve_frame_pointers", FlagValue::Bool(false)));
671
672 match metadata.check_compatible(&engine) {
673 Ok(_) => unreachable!(),
674 Err(e) => assert!(format!("{e:?}").starts_with(
675 "\
676compilation settings of module incompatible with native host
677
678Caused by:
679 setting \"preserve_frame_pointers\" is configured to Bool(false) which is not supported"
680 )),
681 }
682
683 Ok(())
684 }
685
686 #[test]
687 fn test_isa_flags_mismatch() -> Result<()> {
688 let engine = Engine::default();
689 let mut metadata = Metadata::new(&engine);
690
691 metadata
692 .isa_flags
693 .push(("not_a_flag", FlagValue::Bool(true)));
694
695 match metadata.check_compatible(&engine) {
696 Ok(_) => unreachable!(),
697 Err(e) => assert!(
698 format!("{e:?}").starts_with(
699 "\
700compilation settings of module incompatible with native host
701
702Caused by:
703 don't know how to test for target-specific flag \"not_a_flag\" at runtime",
704 ),
705 "bad error {e:?}",
706 ),
707 }
708
709 Ok(())
710 }
711
712 #[test]
713 #[cfg_attr(miri, ignore)]
714 #[cfg(target_pointer_width = "64")] fn test_tunables_int_mismatch() -> Result<()> {
716 let engine = Engine::default();
717 let mut metadata = Metadata::new(&engine);
718
719 metadata.tunables.memory_guard_size = 0;
720
721 match metadata.check_compatible(&engine) {
722 Ok(_) => unreachable!(),
723 Err(e) => assert_eq!(e.to_string(), "Module was compiled with a memory guard size of '0' but '33554432' is expected for the host"),
724 }
725
726 Ok(())
727 }
728
729 #[test]
730 fn test_tunables_bool_mismatch() -> Result<()> {
731 let mut config = Config::new();
732 config.epoch_interruption(true);
733
734 let engine = Engine::new(&config)?;
735 let mut metadata = Metadata::new(&engine);
736 metadata.tunables.epoch_interruption = false;
737
738 match metadata.check_compatible(&engine) {
739 Ok(_) => unreachable!(),
740 Err(e) => assert_eq!(
741 e.to_string(),
742 "Module was compiled without epoch interruption but it is enabled for the host"
743 ),
744 }
745
746 let mut config = Config::new();
747 config.epoch_interruption(false);
748
749 let engine = Engine::new(&config)?;
750 let mut metadata = Metadata::new(&engine);
751 metadata.tunables.epoch_interruption = true;
752
753 match metadata.check_compatible(&engine) {
754 Ok(_) => unreachable!(),
755 Err(e) => assert_eq!(
756 e.to_string(),
757 "Module was compiled with epoch interruption but it is not enabled for the host"
758 ),
759 }
760
761 Ok(())
762 }
763
764 #[test]
765 #[cfg(target_arch = "x86_64")] fn test_feature_mismatch() -> Result<()> {
768 let mut config = Config::new();
769 config.wasm_threads(true);
770
771 let engine = Engine::new(&config)?;
772 let mut metadata = Metadata::new(&engine);
773 metadata.features.threads = false;
774
775 match metadata.check_compatible(&engine) {
776 Ok(_) => unreachable!(),
777 Err(e) => assert_eq!(e.to_string(), "Module was compiled without WebAssembly threads support but it is enabled for the host"),
778 }
779
780 let mut config = Config::new();
781 config.wasm_threads(false);
782
783 let engine = Engine::new(&config)?;
784 let mut metadata = Metadata::new(&engine);
785 metadata.features.threads = true;
786
787 match metadata.check_compatible(&engine) {
788 Ok(_) => unreachable!(),
789 Err(e) => assert_eq!(e.to_string(), "Module was compiled with WebAssembly threads support but it is not enabled for the host"),
790 }
791
792 Ok(())
793 }
794
795 #[test]
796 fn engine_weak_upgrades() {
797 let engine = Engine::default();
798 let weak = engine.weak();
799 weak.upgrade()
800 .expect("engine is still alive, so weak reference can upgrade");
801 drop(engine);
802 assert!(
803 weak.upgrade().is_none(),
804 "engine was dropped, so weak reference cannot upgrade"
805 );
806 }
807
808 #[test]
809 #[cfg_attr(miri, ignore)]
810 fn cache_accounts_for_opt_level() -> Result<()> {
811 let td = TempDir::new()?;
812 let config_path = td.path().join("config.toml");
813 std::fs::write(
814 &config_path,
815 &format!(
816 "
817 [cache]
818 enabled = true
819 directory = '{}'
820 ",
821 td.path().join("cache").display()
822 ),
823 )?;
824 let mut cfg = Config::new();
825 cfg.cranelift_opt_level(OptLevel::None)
826 .cache_config_load(&config_path)?;
827 let engine = Engine::new(&cfg)?;
828 Module::new(&engine, "(module (func))")?;
829 assert_eq!(engine.config().cache_config.cache_hits(), 0);
830 assert_eq!(engine.config().cache_config.cache_misses(), 1);
831 Module::new(&engine, "(module (func))")?;
832 assert_eq!(engine.config().cache_config.cache_hits(), 1);
833 assert_eq!(engine.config().cache_config.cache_misses(), 1);
834
835 let mut cfg = Config::new();
836 cfg.cranelift_opt_level(OptLevel::Speed)
837 .cache_config_load(&config_path)?;
838 let engine = Engine::new(&cfg)?;
839 Module::new(&engine, "(module (func))")?;
840 assert_eq!(engine.config().cache_config.cache_hits(), 0);
841 assert_eq!(engine.config().cache_config.cache_misses(), 1);
842 Module::new(&engine, "(module (func))")?;
843 assert_eq!(engine.config().cache_config.cache_hits(), 1);
844 assert_eq!(engine.config().cache_config.cache_misses(), 1);
845
846 let mut cfg = Config::new();
847 cfg.cranelift_opt_level(OptLevel::SpeedAndSize)
848 .cache_config_load(&config_path)?;
849 let engine = Engine::new(&cfg)?;
850 Module::new(&engine, "(module (func))")?;
851 assert_eq!(engine.config().cache_config.cache_hits(), 0);
852 assert_eq!(engine.config().cache_config.cache_misses(), 1);
853 Module::new(&engine, "(module (func))")?;
854 assert_eq!(engine.config().cache_config.cache_hits(), 1);
855 assert_eq!(engine.config().cache_config.cache_misses(), 1);
856
857 let mut cfg = Config::new();
858 cfg.debug_info(true).cache_config_load(&config_path)?;
859 let engine = Engine::new(&cfg)?;
860 Module::new(&engine, "(module (func))")?;
861 assert_eq!(engine.config().cache_config.cache_hits(), 0);
862 assert_eq!(engine.config().cache_config.cache_misses(), 1);
863 Module::new(&engine, "(module (func))")?;
864 assert_eq!(engine.config().cache_config.cache_hits(), 1);
865 assert_eq!(engine.config().cache_config.cache_misses(), 1);
866
867 Ok(())
868 }
869
870 #[test]
871 fn precompile_compatibility_key_accounts_for_opt_level() {
872 fn hash_for_config(cfg: &Config) -> u64 {
873 let engine = Engine::new(cfg).expect("Config should be valid");
874 let mut hasher = DefaultHasher::new();
875 engine.precompile_compatibility_hash().hash(&mut hasher);
876 hasher.finish()
877 }
878 let mut cfg = Config::new();
879 cfg.cranelift_opt_level(OptLevel::None);
880 let opt_none_hash = hash_for_config(&cfg);
881 cfg.cranelift_opt_level(OptLevel::Speed);
882 let opt_speed_hash = hash_for_config(&cfg);
883 assert_ne!(opt_none_hash, opt_speed_hash)
884 }
885
886 #[test]
887 fn precompile_compatibility_key_accounts_for_module_version_strategy() -> Result<()> {
888 fn hash_for_config(cfg: &Config) -> u64 {
889 let engine = Engine::new(cfg).expect("Config should be valid");
890 let mut hasher = DefaultHasher::new();
891 engine.precompile_compatibility_hash().hash(&mut hasher);
892 hasher.finish()
893 }
894 let mut cfg_custom_version = Config::new();
895 cfg_custom_version.module_version(ModuleVersionStrategy::Custom("1.0.1111".to_string()))?;
896 let custom_version_hash = hash_for_config(&cfg_custom_version);
897
898 let mut cfg_default_version = Config::new();
899 cfg_default_version.module_version(ModuleVersionStrategy::WasmtimeVersion)?;
900 let default_version_hash = hash_for_config(&cfg_default_version);
901
902 let mut cfg_none_version = Config::new();
903 cfg_none_version.module_version(ModuleVersionStrategy::None)?;
904 let none_version_hash = hash_for_config(&cfg_none_version);
905
906 assert_ne!(custom_version_hash, default_version_hash);
907 assert_ne!(custom_version_hash, none_version_hash);
908 assert_ne!(default_version_hash, none_version_hash);
909
910 Ok(())
911 }
912
913 #[test]
914 #[cfg_attr(miri, ignore)]
915 #[cfg(feature = "component-model")]
916 fn components_are_cached() -> Result<()> {
917 use crate::component::Component;
918
919 let td = TempDir::new()?;
920 let config_path = td.path().join("config.toml");
921 std::fs::write(
922 &config_path,
923 &format!(
924 "
925 [cache]
926 enabled = true
927 directory = '{}'
928 ",
929 td.path().join("cache").display()
930 ),
931 )?;
932 let mut cfg = Config::new();
933 cfg.cache_config_load(&config_path)?;
934 let engine = Engine::new(&cfg)?;
935 Component::new(&engine, "(component (core module (func)))")?;
936 assert_eq!(engine.config().cache_config.cache_hits(), 0);
937 assert_eq!(engine.config().cache_config.cache_misses(), 1);
938 Component::new(&engine, "(component (core module (func)))")?;
939 assert_eq!(engine.config().cache_config.cache_hits(), 1);
940 assert_eq!(engine.config().cache_config.cache_misses(), 1);
941
942 Ok(())
943 }
944}