Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/module.rs
1690 views
1
use anyhow::Context;
2
use std::sync::Arc;
3
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
4
use target_lexicon::Triple;
5
use wasmtime::*;
6
use wasmtime_environ::TripleExt;
7
use wasmtime_test_macros::wasmtime_test;
8
9
#[test]
10
fn checks_incompatible_target() -> Result<()> {
11
// For platforms that Cranelift supports make sure a mismatch generates an
12
// error
13
if cfg!(target_arch = "x86_64")
14
|| cfg!(target_arch = "aarch64")
15
|| cfg!(target_arch = "s390x")
16
|| cfg!(target_arch = "riscv64")
17
{
18
let mut target = target_lexicon::Triple::host();
19
target.operating_system = target_lexicon::OperatingSystem::Unknown;
20
assert_invalid_target(&target.to_string())?;
21
}
22
23
// Otherwise make sure that the wrong pulley target is rejected on all
24
// platforms.
25
let wrong_pulley = if cfg!(target_pointer_width = "32") {
26
"pulley64"
27
} else {
28
"pulley32"
29
};
30
assert_invalid_target(wrong_pulley)?;
31
32
return Ok(());
33
34
fn assert_invalid_target(target: &str) -> Result<()> {
35
match Module::new(&Engine::new(Config::new().target(target)?)?, "(module)") {
36
Ok(_) => unreachable!(),
37
Err(e) => assert!(
38
format!("{e:?}").contains("configuration does not match the host"),
39
"bad error: {e:?}"
40
),
41
}
42
43
Ok(())
44
}
45
}
46
47
#[test]
48
fn caches_across_engines() {
49
let c = Config::new();
50
51
let bytes = Module::new(&Engine::new(&c).unwrap(), "(module)")
52
.unwrap()
53
.serialize()
54
.unwrap();
55
56
unsafe {
57
let res = Module::deserialize(&Engine::default(), &bytes);
58
assert!(res.is_ok());
59
60
// differ in runtime settings
61
let res = Module::deserialize(
62
&Engine::new(Config::new().memory_reservation(0)).unwrap(),
63
&bytes,
64
);
65
assert!(res.is_err());
66
67
// differ in wasm features enabled (which can affect
68
// runtime/compilation settings)
69
let res = Module::deserialize(
70
&Engine::new(Config::new().wasm_relaxed_simd(false)).unwrap(),
71
&bytes,
72
);
73
assert!(res.is_err());
74
}
75
}
76
77
#[test]
78
#[cfg_attr(miri, ignore)]
79
fn aot_compiles() -> Result<()> {
80
let engine = Engine::default();
81
let bytes = engine.precompile_module(
82
"(module (func (export \"f\") (param i32) (result i32) local.get 0))".as_bytes(),
83
)?;
84
85
let module = unsafe { Module::deserialize(&engine, &bytes)? };
86
87
let mut store = Store::new(&engine, ());
88
let instance = Instance::new(&mut store, &module, &[])?;
89
90
let f = instance.get_typed_func::<i32, i32>(&mut store, "f")?;
91
assert_eq!(f.call(&mut store, 101)?, 101);
92
93
Ok(())
94
}
95
96
#[test]
97
#[cfg_attr(miri, ignore)]
98
fn serialize_deterministic() {
99
let engine = Engine::default();
100
101
let assert_deterministic = |wasm: &str| {
102
let p1 = engine.precompile_module(wasm.as_bytes()).unwrap();
103
let p2 = engine.precompile_module(wasm.as_bytes()).unwrap();
104
if p1 != p2 {
105
panic!("precompile_module not deterministic for:\n{wasm}");
106
}
107
108
let module1 = Module::new(&engine, wasm).unwrap();
109
let a1 = module1.serialize().unwrap();
110
let a2 = module1.serialize().unwrap();
111
if a1 != a2 {
112
panic!("Module::serialize not deterministic for:\n{wasm}");
113
}
114
115
let module2 = Module::new(&engine, wasm).unwrap();
116
let b1 = module2.serialize().unwrap();
117
let b2 = module2.serialize().unwrap();
118
if b1 != b2 {
119
panic!("Module::serialize not deterministic for:\n{wasm}");
120
}
121
122
if a1 != b2 {
123
panic!("not matching across modules:\n{wasm}");
124
}
125
if b1 != p2 {
126
panic!("not matching across engine/module:\n{wasm}");
127
}
128
};
129
130
assert_deterministic("(module)");
131
assert_deterministic("(module (func))");
132
assert_deterministic("(module (func nop))");
133
assert_deterministic("(module (func) (func (param i32)))");
134
assert_deterministic("(module (func (export \"f\")) (func (export \"y\")))");
135
assert_deterministic("(module (func $f) (func $g))");
136
assert_deterministic("(module (data \"\") (data \"\"))");
137
assert_deterministic("(module (elem func) (elem func))");
138
}
139
140
// This test asserts that the optimization to transform separate data segments
141
// into an initialization image doesn't unnecessarily create a massive module by
142
// accident with a very large initialization image in it.
143
#[test]
144
fn serialize_not_overly_massive() -> Result<()> {
145
let mut config = Config::new();
146
config.memory_guaranteed_dense_image_size(1 << 20);
147
let engine = Engine::new(&config)?;
148
149
let assert_smaller_than_1mb = |module: &str| -> Result<()> {
150
println!("{module}");
151
let bytes = Module::new(&engine, module)?.serialize()?;
152
assert!(bytes.len() < (1 << 20));
153
Ok(())
154
};
155
156
// Tons of space between data segments should use sparse initialization,
157
// along with various permutations of empty and nonempty segments.
158
assert_smaller_than_1mb(
159
r#"(module
160
(memory 20000)
161
(data (i32.const 0) "a")
162
(data (i32.const 0x200000) "b")
163
)"#,
164
)?;
165
assert_smaller_than_1mb(
166
r#"(module
167
(memory 20000)
168
(data (i32.const 0) "a")
169
(data (i32.const 0x200000) "")
170
)"#,
171
)?;
172
assert_smaller_than_1mb(
173
r#"(module
174
(memory 20000)
175
(data (i32.const 0) "")
176
(data (i32.const 0x200000) "b")
177
)"#,
178
)?;
179
assert_smaller_than_1mb(
180
r#"(module
181
(memory 20000)
182
(data (i32.const 0) "")
183
(data (i32.const 0x200000) "")
184
)"#,
185
)?;
186
187
// lone data segment
188
assert_smaller_than_1mb(
189
r#"(module
190
(memory 20000)
191
(data (i32.const 0x200000) "b")
192
)"#,
193
)?;
194
195
Ok(())
196
}
197
198
// This test specifically disables SSE4.1 in Cranelift which force wasm
199
// instructions like `f32.ceil` to go through libcalls instead of using native
200
// instructions. Note that SIMD is also disabled here because SIMD otherwise
201
// requires SSE4.1 to be enabled.
202
//
203
// This test then also tests that loading modules through various means, e.g.
204
// through precompiled artifacts, all works.
205
#[test]
206
#[cfg_attr(any(not(target_arch = "x86_64"), miri), ignore)]
207
fn missing_sse_and_floats_still_works() -> Result<()> {
208
let mut config = Config::new();
209
config.wasm_simd(false).wasm_relaxed_simd(false);
210
unsafe {
211
config.cranelift_flag_set("has_sse41", "false");
212
}
213
let engine = Engine::new(&config)?;
214
let module = Module::new(
215
&engine,
216
r#"
217
(module
218
(func (export "f32.ceil") (param f32) (result f32)
219
local.get 0
220
f32.ceil)
221
)
222
"#,
223
)?;
224
let bytes = module.serialize()?;
225
let module2 = unsafe { Module::deserialize(&engine, &bytes)? };
226
let tmpdir = tempfile::TempDir::new()?;
227
let path = tmpdir.path().join("module.cwasm");
228
std::fs::write(&path, &bytes)?;
229
let module3 = unsafe { Module::deserialize_file(&engine, &path)? };
230
231
for module in [module, module2, module3] {
232
let mut store = Store::new(&engine, ());
233
let instance = Instance::new(&mut store, &module, &[])?;
234
let ceil = instance.get_typed_func::<f32, f32>(&mut store, "f32.ceil")?;
235
236
for f in [1.0, 2.3, -1.3] {
237
assert_eq!(ceil.call(&mut store, f)?, f.ceil());
238
}
239
}
240
241
Ok(())
242
}
243
244
#[test]
245
#[cfg_attr(miri, ignore)]
246
fn large_add_chain_no_stack_overflow() -> Result<()> {
247
let mut config = Config::new();
248
config.cranelift_opt_level(OptLevel::None);
249
let engine = Engine::new(&config)?;
250
let mut wat = String::from(
251
"
252
(module
253
(func (result i64)
254
(i64.const 1)
255
",
256
);
257
for _ in 0..20_000 {
258
wat.push_str("(i64.add (i64.const 1))\n");
259
}
260
261
wat.push_str(")\n)");
262
Module::new(&engine, &wat)?;
263
264
Ok(())
265
}
266
267
#[test]
268
fn compile_a_component() -> Result<()> {
269
let engine = Engine::default();
270
let err = Module::new(&engine, "(component)").unwrap_err();
271
let err = format!("{err:?}");
272
assert!(
273
err.contains("expected a WebAssembly module but was given a WebAssembly component"),
274
"bad error: {err}"
275
);
276
Ok(())
277
}
278
279
#[test]
280
#[cfg_attr(miri, ignore)]
281
fn tail_call_defaults() -> Result<()> {
282
let wasm_with_tail_calls = "(module (func $a return_call $a))";
283
284
// on by default
285
Module::new(&Engine::default(), wasm_with_tail_calls)?;
286
287
// on by default for cranelift
288
Module::new(
289
&Engine::new(Config::new().strategy(Strategy::Cranelift))?,
290
wasm_with_tail_calls,
291
)?;
292
293
if cfg!(target_arch = "x86_64") {
294
// off by default for winch
295
let err = Module::new(
296
&Engine::new(Config::new().strategy(Strategy::Winch))?,
297
wasm_with_tail_calls,
298
);
299
assert!(err.is_err());
300
}
301
Ok(())
302
}
303
304
#[test]
305
#[cfg_attr(miri, ignore)]
306
fn cross_engine_module_exports() -> Result<()> {
307
let a_engine = Engine::default();
308
let b_engine = Engine::default();
309
310
let a_module = Module::new(&a_engine, "(module)")?;
311
let b_module = Module::new(
312
&b_engine,
313
r#"
314
(module
315
(func (export "x"))
316
)
317
"#,
318
)?;
319
320
let export = b_module.get_export_index("x").unwrap();
321
322
let mut store = Store::new(&a_engine, ());
323
let instance = Instance::new(&mut store, &a_module, &[])?;
324
assert!(instance.get_module_export(&mut store, &export).is_none());
325
Ok(())
326
}
327
328
/// Smoke test for registering and unregistering modules (and their rec group
329
/// entries) concurrently.
330
#[wasmtime_test(wasm_features(gc, function_references))]
331
#[cfg_attr(miri, ignore)]
332
fn concurrent_type_registry_modifications(config: &mut Config) -> Result<()> {
333
let _ = env_logger::try_init();
334
335
// The number of seconds to run the smoke test.
336
const TEST_DURATION_SECONDS: u64 = 5;
337
338
// The number of worker threads to spawn for this smoke test.
339
const NUM_WORKER_THREADS: usize = 32;
340
341
let engine = Engine::new(config)?;
342
343
/// Tests of various kinds of modifications to the type registry.
344
enum Test {
345
/// Creating a module (from its text format) should register new entries
346
/// in the type registry.
347
Module(&'static str),
348
/// Creating an individual func type registers a singleton entry in the
349
/// registry which is managed slightly differently from modules.
350
Func(fn(&Engine) -> FuncType),
351
/// Create a single struct type like a single function type.
352
Struct(fn(&Engine) -> StructType),
353
/// Create a single array type like a single function type.
354
Array(fn(&Engine) -> ArrayType),
355
}
356
const TESTS: &'static [Test] = &[
357
Test::Func(|engine| FuncType::new(engine, [], [])),
358
Test::Func(|engine| FuncType::new(engine, [], [ValType::I32])),
359
Test::Func(|engine| FuncType::new(engine, [ValType::I32], [])),
360
Test::Struct(|engine| StructType::new(engine, []).unwrap()),
361
Test::Array(|engine| {
362
ArrayType::new(engine, FieldType::new(Mutability::Const, StorageType::I8))
363
}),
364
Test::Array(|engine| {
365
ArrayType::new(engine, FieldType::new(Mutability::Var, StorageType::I8))
366
}),
367
Test::Module(
368
r#"
369
(module
370
;; A handful of function types.
371
(type (func))
372
(type (func (param i32)))
373
(type (func (result i32)))
374
(type (func (param i32) (result i32)))
375
376
;; A handful of recursive types.
377
(rec)
378
(rec (type $s (struct (field (ref null $s)))))
379
(rec (type $a (struct (field (ref null $b))))
380
(type $b (struct (field (ref null $a)))))
381
(rec (type $c (struct (field (ref null $b))
382
(field (ref null $d))))
383
(type $d (struct (field (ref null $a))
384
(field (ref null $c)))))
385
386
;; Some GC types
387
(type (struct))
388
(type (array i8))
389
(type (array (mut i8)))
390
)
391
"#,
392
),
393
Test::Module(
394
r#"
395
(module
396
;; Just the function types.
397
(type (func))
398
(type (func (param i32)))
399
(type (func (result i32)))
400
(type (func (param i32) (result i32)))
401
)
402
"#,
403
),
404
Test::Module(
405
r#"
406
(module
407
;; Just the recursive types.
408
(rec)
409
(rec (type $s (struct (field (ref null $s)))))
410
(rec (type $a (struct (field (ref null $b))))
411
(type $b (struct (field (ref null $a)))))
412
(rec (type $c (struct (field (ref null $b))
413
(field (ref null $d))))
414
(type $d (struct (field (ref null $a))
415
(field (ref null $c)))))
416
)
417
"#,
418
),
419
Test::Module(
420
r#"
421
(module
422
;; One of each kind of type.
423
(type (func (param i32) (result i32)))
424
(rec (type $a (struct (field (ref null $b))))
425
(type $b (struct (field (ref null $a)))))
426
)
427
"#,
428
),
429
];
430
431
// Spawn the worker threads, each of them just registering and unregistering
432
// modules (and their types) constantly for the duration of the smoke test.
433
let handles = (0..NUM_WORKER_THREADS)
434
.map(|_| {
435
let engine = engine.clone();
436
std::thread::spawn(move || -> Result<()> {
437
let mut tests = TESTS.iter().cycle();
438
let start = std::time::Instant::now();
439
while start.elapsed().as_secs() < TEST_DURATION_SECONDS {
440
match tests.next() {
441
Some(Test::Module(wat)) => {
442
let _ = Module::new(&engine, wat)?;
443
}
444
Some(Test::Func(ctor)) => {
445
let _ = ctor(&engine);
446
}
447
Some(Test::Struct(ctor)) => {
448
let _ = ctor(&engine);
449
}
450
Some(Test::Array(ctor)) => {
451
let _ = ctor(&engine);
452
}
453
None => unreachable!(),
454
}
455
}
456
457
Ok(())
458
})
459
})
460
.collect::<Vec<_>>();
461
462
// Join all of the thread handles.
463
for handle in handles {
464
handle
465
.join()
466
.expect("should join thread handle")
467
.context("error during thread execution")?;
468
}
469
470
Ok(())
471
}
472
473
#[wasmtime_test(wasm_features(function_references))]
474
#[cfg_attr(miri, ignore)]
475
fn concurrent_type_modifications_and_checks(config: &mut Config) -> Result<()> {
476
const THREADS_CHECKING: usize = 4;
477
478
let _ = env_logger::try_init();
479
480
let engine = Engine::new(&config)?;
481
482
let mut threads = Vec::new();
483
let keep_going = Arc::new(AtomicBool::new(true));
484
485
// Spawn a number of threads that are all working with a module and testing
486
// various properties about type-checks in the module.
487
for _ in 0..THREADS_CHECKING {
488
threads.push(std::thread::spawn({
489
let engine = engine.clone();
490
let keep_going = keep_going.clone();
491
move || -> Result<()> {
492
while keep_going.load(Relaxed) {
493
let module = Module::new(
494
&engine,
495
r#"
496
(module
497
(func (export "f") (param funcref)
498
i32.const 0
499
local.get 0
500
table.set
501
i32.const 0
502
call_indirect (result f64)
503
drop
504
)
505
506
(table 1 funcref)
507
)
508
"#,
509
)?;
510
let ty = FuncType::new(&engine, [], [ValType::I32]);
511
let mut store = Store::new(&engine, ());
512
let func = Func::new(&mut store, ty, |_, _, results| {
513
results[0] = Val::I32(0);
514
Ok(())
515
});
516
517
let instance = Instance::new(&mut store, &module, &[])?;
518
assert!(instance.get_typed_func::<(), i32>(&mut store, "f").is_err());
519
assert!(instance.get_typed_func::<(), f64>(&mut store, "f").is_err());
520
let f = instance.get_typed_func::<Func, ()>(&mut store, "f")?;
521
let err = f.call(&mut store, func).unwrap_err();
522
assert_eq!(err.downcast::<Trap>()?, Trap::BadSignature);
523
}
524
Ok(())
525
}
526
}));
527
}
528
529
// Spawn threads in the background creating/destroying `FuncType`s related
530
// to the module above.
531
threads.push(std::thread::spawn({
532
let engine = engine.clone();
533
let keep_going = keep_going.clone();
534
move || -> Result<()> {
535
while keep_going.load(Relaxed) {
536
FuncType::new(&engine, [], [ValType::F64]);
537
}
538
Ok(())
539
}
540
}));
541
threads.push(std::thread::spawn({
542
let engine = engine.clone();
543
let keep_going = keep_going.clone();
544
move || -> Result<()> {
545
while keep_going.load(Relaxed) {
546
FuncType::new(&engine, [], [ValType::I32]);
547
}
548
Ok(())
549
}
550
}));
551
552
std::thread::sleep(std::time::Duration::new(2, 0));
553
keep_going.store(false, Relaxed);
554
555
for thread in threads {
556
thread.join().unwrap()?;
557
}
558
559
Ok(())
560
}
561
562
#[test]
563
#[cfg_attr(miri, ignore)]
564
fn validate_deterministic() {
565
let mut faulty_wat = "(module ".to_string();
566
for i in 0..100 {
567
faulty_wat.push_str(&format!(
568
"(func (export \"foo_{i}\") (result i64) (i64.add (i32.const 0) (i64.const 1)))"
569
));
570
}
571
faulty_wat.push_str(")");
572
let binary = wat::parse_str(faulty_wat).unwrap();
573
574
let engine_parallel = Engine::new(&Config::new().parallel_compilation(true)).unwrap();
575
let result_parallel = Module::validate(&engine_parallel, &binary)
576
.unwrap_err()
577
.to_string();
578
579
let engine_sequential = Engine::new(&Config::new().parallel_compilation(false)).unwrap();
580
let result_sequential = Module::validate(&engine_sequential, &binary)
581
.unwrap_err()
582
.to_string();
583
assert_eq!(result_parallel, result_sequential);
584
}
585
586
#[test]
587
#[cfg_attr(miri, ignore)]
588
fn deserialize_raw_avoids_copy() {
589
// target pulley; executing code directly requires virtual memory
590
let mut config = Config::new();
591
let target = format!("{}", Triple::pulley_host());
592
config.target(&target).unwrap();
593
let engine = Engine::new(&config).unwrap();
594
let wat = String::from(
595
r#"
596
(module
597
(func (export "add") (param $lhs i32) (param $rhs i32) (result i32)
598
(i32.add (local.get $lhs) (local.get $rhs))
599
)
600
)
601
"#,
602
);
603
let module = Module::new(&engine, &wat).unwrap();
604
let mut serialized = module.serialize().expect("Serialize failed");
605
let serialized_ptr = std::ptr::slice_from_raw_parts(serialized.as_mut_ptr(), serialized.len());
606
let module_memory = std::ptr::NonNull::new(serialized_ptr.cast_mut()).unwrap();
607
let deserialized_module =
608
unsafe { Module::deserialize_raw(&engine, module_memory).expect("Deserialize Failed") };
609
610
// assert that addresses haven't changed, no full copy
611
// TODO: haven't been able to find a pub way of doing this
612
613
// basic verification that the loaded module works
614
let mut store = Store::new(&engine, ());
615
let instance = Instance::new(&mut store, &deserialized_module, &[]).unwrap();
616
let f = instance
617
.get_typed_func::<(i32, i32), i32>(&mut store, "add")
618
.unwrap();
619
assert_eq!(f.call(&mut store, (26, 50)).unwrap(), 76);
620
}
621
622
#[test]
623
#[cfg_attr(miri, ignore)]
624
fn deserialize_raw_fails_for_native() {
625
// target pulley
626
let engine = Engine::default();
627
let wat = String::from(
628
r#"
629
(module
630
(func (export "add") (param $lhs i32) (param $rhs i32) (result i32)
631
(i32.add (local.get $lhs) (local.get $rhs))
632
)
633
)
634
"#,
635
);
636
let module = Module::new(&engine, &wat).unwrap();
637
let mut serialized = module.serialize().expect("Serialize failed");
638
let serialized_ptr = std::ptr::slice_from_raw_parts(serialized.as_mut_ptr(), serialized.len());
639
let module_memory = std::ptr::NonNull::new(serialized_ptr.cast_mut()).unwrap();
640
let deserialize_res = unsafe { Module::deserialize_raw(&engine, module_memory) };
641
642
if engine.is_pulley() {
643
let _mod = deserialize_res.expect("Module should deserialize fine for pulley");
644
} else {
645
let err = deserialize_res.expect_err("Deserialization should fail for host target");
646
assert_eq!(
647
format!("{err}"),
648
"this target requires virtual memory to be enabled"
649
);
650
}
651
}
652
653