Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/component_model/func.rs
1692 views
1
#![cfg(not(miri))]
2
3
use super::{REALLOC_AND_FREE, TypedFuncExt};
4
use anyhow::Result;
5
use std::sync::Arc;
6
use wasmtime::component::*;
7
use wasmtime::{Config, Engine, Store, StoreContextMut, Trap};
8
9
const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;
10
const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000;
11
12
#[test]
13
fn thunks() -> Result<()> {
14
let component = r#"
15
(component
16
(core module $m
17
(func (export "thunk"))
18
(func (export "thunk-trap") unreachable)
19
)
20
(core instance $i (instantiate $m))
21
(func (export "thunk")
22
(canon lift (core func $i "thunk"))
23
)
24
(func (export "thunk-trap")
25
(canon lift (core func $i "thunk-trap"))
26
)
27
)
28
"#;
29
30
let engine = super::engine();
31
let component = Component::new(&engine, component)?;
32
let mut store = Store::new(&engine, ());
33
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
34
instance
35
.get_typed_func::<(), ()>(&mut store, "thunk")?
36
.call_and_post_return(&mut store, ())?;
37
let err = instance
38
.get_typed_func::<(), ()>(&mut store, "thunk-trap")?
39
.call(&mut store, ())
40
.unwrap_err();
41
assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached);
42
43
Ok(())
44
}
45
46
#[test]
47
fn typecheck() -> Result<()> {
48
let component = r#"
49
(component
50
(core module $m
51
(func (export "thunk"))
52
(func (export "take-string") (param i32 i32))
53
(func (export "two-args") (param i32 i32 i32))
54
(func (export "ret-one") (result i32) unreachable)
55
56
(memory (export "memory") 1)
57
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
58
unreachable)
59
)
60
(core instance $i (instantiate (module $m)))
61
(func (export "thunk")
62
(canon lift (core func $i "thunk"))
63
)
64
(func (export "take-string") (param "a" string)
65
(canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc")))
66
)
67
(func (export "take-two-args") (param "a" s32) (param "b" (list u8))
68
(canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc")))
69
)
70
(func (export "ret-tuple") (result (tuple u8 s8))
71
(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
72
)
73
(func (export "ret-tuple1") (result (tuple u32))
74
(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
75
)
76
(func (export "ret-string") (result string)
77
(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
78
)
79
(func (export "ret-list-u8") (result (list u8))
80
(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
81
)
82
)
83
"#;
84
85
let engine = Engine::default();
86
let component = Component::new(&engine, component)?;
87
let mut store = Store::new(&engine, ());
88
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
89
let thunk = instance.get_func(&mut store, "thunk").unwrap();
90
let take_string = instance.get_func(&mut store, "take-string").unwrap();
91
let take_two_args = instance.get_func(&mut store, "take-two-args").unwrap();
92
let ret_tuple = instance.get_func(&mut store, "ret-tuple").unwrap();
93
let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap();
94
let ret_string = instance.get_func(&mut store, "ret-string").unwrap();
95
let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap();
96
assert!(thunk.typed::<(), (u32,)>(&store).is_err());
97
assert!(thunk.typed::<(u32,), ()>(&store).is_err());
98
assert!(thunk.typed::<(), ()>(&store).is_ok());
99
assert!(take_string.typed::<(), ()>(&store).is_err());
100
assert!(take_string.typed::<(String,), ()>(&store).is_ok());
101
assert!(take_string.typed::<(&str,), ()>(&store).is_ok());
102
assert!(take_string.typed::<(&[u8],), ()>(&store).is_err());
103
assert!(take_two_args.typed::<(), ()>(&store).is_err());
104
assert!(take_two_args.typed::<(i32, &[u8]), (u32,)>(&store).is_err());
105
assert!(take_two_args.typed::<(u32, &[u8]), ()>(&store).is_err());
106
assert!(take_two_args.typed::<(i32, &[u8]), ()>(&store).is_ok());
107
assert!(ret_tuple.typed::<(), ()>(&store).is_err());
108
assert!(ret_tuple.typed::<(), (u8,)>(&store).is_err());
109
assert!(ret_tuple.typed::<(), ((u8, i8),)>(&store).is_ok());
110
assert!(ret_tuple1.typed::<(), ((u32,),)>(&store).is_ok());
111
assert!(ret_tuple1.typed::<(), (u32,)>(&store).is_err());
112
assert!(ret_string.typed::<(), ()>(&store).is_err());
113
assert!(ret_string.typed::<(), (WasmStr,)>(&store).is_ok());
114
assert!(ret_list_u8.typed::<(), (WasmList<u16>,)>(&store).is_err());
115
assert!(ret_list_u8.typed::<(), (WasmList<i8>,)>(&store).is_err());
116
assert!(ret_list_u8.typed::<(), (WasmList<u8>,)>(&store).is_ok());
117
118
Ok(())
119
}
120
121
#[test]
122
fn integers() -> Result<()> {
123
let component = r#"
124
(component
125
(core module $m
126
(func (export "take-i32-100") (param i32)
127
local.get 0
128
i32.const 100
129
i32.eq
130
br_if 0
131
unreachable
132
)
133
(func (export "take-i64-100") (param i64)
134
local.get 0
135
i64.const 100
136
i64.eq
137
br_if 0
138
unreachable
139
)
140
(func (export "ret-i32-0") (result i32) i32.const 0)
141
(func (export "ret-i64-0") (result i64) i64.const 0)
142
(func (export "ret-i32-minus-1") (result i32) i32.const -1)
143
(func (export "ret-i64-minus-1") (result i64) i64.const -1)
144
(func (export "ret-i32-100000") (result i32) i32.const 100000)
145
)
146
(core instance $i (instantiate (module $m)))
147
(func (export "take-u8") (param "a" u8) (canon lift (core func $i "take-i32-100")))
148
(func (export "take-s8") (param "a" s8) (canon lift (core func $i "take-i32-100")))
149
(func (export "take-u16") (param "a" u16) (canon lift (core func $i "take-i32-100")))
150
(func (export "take-s16") (param "a" s16) (canon lift (core func $i "take-i32-100")))
151
(func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100")))
152
(func (export "take-s32") (param "a" s32) (canon lift (core func $i "take-i32-100")))
153
(func (export "take-u64") (param "a" u64) (canon lift (core func $i "take-i64-100")))
154
(func (export "take-s64") (param "a" s64) (canon lift (core func $i "take-i64-100")))
155
156
(func (export "ret-u8") (result u8) (canon lift (core func $i "ret-i32-0")))
157
(func (export "ret-s8") (result s8) (canon lift (core func $i "ret-i32-0")))
158
(func (export "ret-u16") (result u16) (canon lift (core func $i "ret-i32-0")))
159
(func (export "ret-s16") (result s16) (canon lift (core func $i "ret-i32-0")))
160
(func (export "ret-u32") (result u32) (canon lift (core func $i "ret-i32-0")))
161
(func (export "ret-s32") (result s32) (canon lift (core func $i "ret-i32-0")))
162
(func (export "ret-u64") (result u64) (canon lift (core func $i "ret-i64-0")))
163
(func (export "ret-s64") (result s64) (canon lift (core func $i "ret-i64-0")))
164
165
(func (export "retm1-u8") (result u8) (canon lift (core func $i "ret-i32-minus-1")))
166
(func (export "retm1-s8") (result s8) (canon lift (core func $i "ret-i32-minus-1")))
167
(func (export "retm1-u16") (result u16) (canon lift (core func $i "ret-i32-minus-1")))
168
(func (export "retm1-s16") (result s16) (canon lift (core func $i "ret-i32-minus-1")))
169
(func (export "retm1-u32") (result u32) (canon lift (core func $i "ret-i32-minus-1")))
170
(func (export "retm1-s32") (result s32) (canon lift (core func $i "ret-i32-minus-1")))
171
(func (export "retm1-u64") (result u64) (canon lift (core func $i "ret-i64-minus-1")))
172
(func (export "retm1-s64") (result s64) (canon lift (core func $i "ret-i64-minus-1")))
173
174
(func (export "retbig-u8") (result u8) (canon lift (core func $i "ret-i32-100000")))
175
(func (export "retbig-s8") (result s8) (canon lift (core func $i "ret-i32-100000")))
176
(func (export "retbig-u16") (result u16) (canon lift (core func $i "ret-i32-100000")))
177
(func (export "retbig-s16") (result s16) (canon lift (core func $i "ret-i32-100000")))
178
(func (export "retbig-u32") (result u32) (canon lift (core func $i "ret-i32-100000")))
179
(func (export "retbig-s32") (result s32) (canon lift (core func $i "ret-i32-100000")))
180
)
181
"#;
182
183
let engine = super::engine();
184
let component = Component::new(&engine, component)?;
185
let mut store = Store::new(&engine, ());
186
let new_instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component);
187
let instance = new_instance(&mut store)?;
188
189
// Passing in 100 is valid for all primitives
190
instance
191
.get_typed_func::<(u8,), ()>(&mut store, "take-u8")?
192
.call_and_post_return(&mut store, (100,))?;
193
instance
194
.get_typed_func::<(i8,), ()>(&mut store, "take-s8")?
195
.call_and_post_return(&mut store, (100,))?;
196
instance
197
.get_typed_func::<(u16,), ()>(&mut store, "take-u16")?
198
.call_and_post_return(&mut store, (100,))?;
199
instance
200
.get_typed_func::<(i16,), ()>(&mut store, "take-s16")?
201
.call_and_post_return(&mut store, (100,))?;
202
instance
203
.get_typed_func::<(u32,), ()>(&mut store, "take-u32")?
204
.call_and_post_return(&mut store, (100,))?;
205
instance
206
.get_typed_func::<(i32,), ()>(&mut store, "take-s32")?
207
.call_and_post_return(&mut store, (100,))?;
208
instance
209
.get_typed_func::<(u64,), ()>(&mut store, "take-u64")?
210
.call_and_post_return(&mut store, (100,))?;
211
instance
212
.get_typed_func::<(i64,), ()>(&mut store, "take-s64")?
213
.call_and_post_return(&mut store, (100,))?;
214
215
// This specific wasm instance traps if any value other than 100 is passed
216
new_instance(&mut store)?
217
.get_typed_func::<(u8,), ()>(&mut store, "take-u8")?
218
.call(&mut store, (101,))
219
.unwrap_err()
220
.downcast::<Trap>()?;
221
new_instance(&mut store)?
222
.get_typed_func::<(i8,), ()>(&mut store, "take-s8")?
223
.call(&mut store, (101,))
224
.unwrap_err()
225
.downcast::<Trap>()?;
226
new_instance(&mut store)?
227
.get_typed_func::<(u16,), ()>(&mut store, "take-u16")?
228
.call(&mut store, (101,))
229
.unwrap_err()
230
.downcast::<Trap>()?;
231
new_instance(&mut store)?
232
.get_typed_func::<(i16,), ()>(&mut store, "take-s16")?
233
.call(&mut store, (101,))
234
.unwrap_err()
235
.downcast::<Trap>()?;
236
new_instance(&mut store)?
237
.get_typed_func::<(u32,), ()>(&mut store, "take-u32")?
238
.call(&mut store, (101,))
239
.unwrap_err()
240
.downcast::<Trap>()?;
241
new_instance(&mut store)?
242
.get_typed_func::<(i32,), ()>(&mut store, "take-s32")?
243
.call(&mut store, (101,))
244
.unwrap_err()
245
.downcast::<Trap>()?;
246
new_instance(&mut store)?
247
.get_typed_func::<(u64,), ()>(&mut store, "take-u64")?
248
.call(&mut store, (101,))
249
.unwrap_err()
250
.downcast::<Trap>()?;
251
new_instance(&mut store)?
252
.get_typed_func::<(i64,), ()>(&mut store, "take-s64")?
253
.call(&mut store, (101,))
254
.unwrap_err()
255
.downcast::<Trap>()?;
256
257
// Zero can be returned as any integer
258
assert_eq!(
259
instance
260
.get_typed_func::<(), (u8,)>(&mut store, "ret-u8")?
261
.call_and_post_return(&mut store, ())?,
262
(0,)
263
);
264
assert_eq!(
265
instance
266
.get_typed_func::<(), (i8,)>(&mut store, "ret-s8")?
267
.call_and_post_return(&mut store, ())?,
268
(0,)
269
);
270
assert_eq!(
271
instance
272
.get_typed_func::<(), (u16,)>(&mut store, "ret-u16")?
273
.call_and_post_return(&mut store, ())?,
274
(0,)
275
);
276
assert_eq!(
277
instance
278
.get_typed_func::<(), (i16,)>(&mut store, "ret-s16")?
279
.call_and_post_return(&mut store, ())?,
280
(0,)
281
);
282
assert_eq!(
283
instance
284
.get_typed_func::<(), (u32,)>(&mut store, "ret-u32")?
285
.call_and_post_return(&mut store, ())?,
286
(0,)
287
);
288
assert_eq!(
289
instance
290
.get_typed_func::<(), (i32,)>(&mut store, "ret-s32")?
291
.call_and_post_return(&mut store, ())?,
292
(0,)
293
);
294
assert_eq!(
295
instance
296
.get_typed_func::<(), (u64,)>(&mut store, "ret-u64")?
297
.call_and_post_return(&mut store, ())?,
298
(0,)
299
);
300
assert_eq!(
301
instance
302
.get_typed_func::<(), (i64,)>(&mut store, "ret-s64")?
303
.call_and_post_return(&mut store, ())?,
304
(0,)
305
);
306
307
// Returning -1 should reinterpret the bytes as defined by each type.
308
assert_eq!(
309
instance
310
.get_typed_func::<(), (u8,)>(&mut store, "retm1-u8")?
311
.call_and_post_return(&mut store, ())?,
312
(0xff,)
313
);
314
assert_eq!(
315
instance
316
.get_typed_func::<(), (i8,)>(&mut store, "retm1-s8")?
317
.call_and_post_return(&mut store, ())?,
318
(-1,)
319
);
320
assert_eq!(
321
instance
322
.get_typed_func::<(), (u16,)>(&mut store, "retm1-u16")?
323
.call_and_post_return(&mut store, ())?,
324
(0xffff,)
325
);
326
assert_eq!(
327
instance
328
.get_typed_func::<(), (i16,)>(&mut store, "retm1-s16")?
329
.call_and_post_return(&mut store, ())?,
330
(-1,)
331
);
332
assert_eq!(
333
instance
334
.get_typed_func::<(), (u32,)>(&mut store, "retm1-u32")?
335
.call_and_post_return(&mut store, ())?,
336
(0xffffffff,)
337
);
338
assert_eq!(
339
instance
340
.get_typed_func::<(), (i32,)>(&mut store, "retm1-s32")?
341
.call_and_post_return(&mut store, ())?,
342
(-1,)
343
);
344
assert_eq!(
345
instance
346
.get_typed_func::<(), (u64,)>(&mut store, "retm1-u64")?
347
.call_and_post_return(&mut store, ())?,
348
(0xffffffff_ffffffff,)
349
);
350
assert_eq!(
351
instance
352
.get_typed_func::<(), (i64,)>(&mut store, "retm1-s64")?
353
.call_and_post_return(&mut store, ())?,
354
(-1,)
355
);
356
357
// Returning 100000 should chop off bytes as necessary
358
let ret: u32 = 100000;
359
assert_eq!(
360
instance
361
.get_typed_func::<(), (u8,)>(&mut store, "retbig-u8")?
362
.call_and_post_return(&mut store, ())?,
363
(ret as u8,),
364
);
365
assert_eq!(
366
instance
367
.get_typed_func::<(), (i8,)>(&mut store, "retbig-s8")?
368
.call_and_post_return(&mut store, ())?,
369
(ret as i8,),
370
);
371
assert_eq!(
372
instance
373
.get_typed_func::<(), (u16,)>(&mut store, "retbig-u16")?
374
.call_and_post_return(&mut store, ())?,
375
(ret as u16,),
376
);
377
assert_eq!(
378
instance
379
.get_typed_func::<(), (i16,)>(&mut store, "retbig-s16")?
380
.call_and_post_return(&mut store, ())?,
381
(ret as i16,),
382
);
383
assert_eq!(
384
instance
385
.get_typed_func::<(), (u32,)>(&mut store, "retbig-u32")?
386
.call_and_post_return(&mut store, ())?,
387
(ret,),
388
);
389
assert_eq!(
390
instance
391
.get_typed_func::<(), (i32,)>(&mut store, "retbig-s32")?
392
.call_and_post_return(&mut store, ())?,
393
(ret as i32,),
394
);
395
396
Ok(())
397
}
398
399
#[test]
400
fn type_layers() -> Result<()> {
401
let component = r#"
402
(component
403
(core module $m
404
(func (export "take-i32-100") (param i32)
405
local.get 0
406
i32.const 2
407
i32.eq
408
br_if 0
409
unreachable
410
)
411
)
412
(core instance $i (instantiate $m))
413
(func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100")))
414
)
415
"#;
416
417
let engine = super::engine();
418
let component = Component::new(&engine, component)?;
419
let mut store = Store::new(&engine, ());
420
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
421
422
instance
423
.get_typed_func::<(Box<u32>,), ()>(&mut store, "take-u32")?
424
.call_and_post_return(&mut store, (Box::new(2),))?;
425
instance
426
.get_typed_func::<(&u32,), ()>(&mut store, "take-u32")?
427
.call_and_post_return(&mut store, (&2,))?;
428
instance
429
.get_typed_func::<(Arc<u32>,), ()>(&mut store, "take-u32")?
430
.call_and_post_return(&mut store, (Arc::new(2),))?;
431
instance
432
.get_typed_func::<(&Box<Arc<Box<u32>>>,), ()>(&mut store, "take-u32")?
433
.call_and_post_return(&mut store, (&Box::new(Arc::new(Box::new(2))),))?;
434
435
Ok(())
436
}
437
438
#[test]
439
fn floats() -> Result<()> {
440
let component = r#"
441
(component
442
(core module $m
443
(func (export "i32.reinterpret_f32") (param f32) (result i32)
444
local.get 0
445
i32.reinterpret_f32
446
)
447
(func (export "i64.reinterpret_f64") (param f64) (result i64)
448
local.get 0
449
i64.reinterpret_f64
450
)
451
(func (export "f32.reinterpret_i32") (param i32) (result f32)
452
local.get 0
453
f32.reinterpret_i32
454
)
455
(func (export "f64.reinterpret_i64") (param i64) (result f64)
456
local.get 0
457
f64.reinterpret_i64
458
)
459
)
460
(core instance $i (instantiate $m))
461
462
(func (export "f32-to-u32") (param "a" float32) (result u32)
463
(canon lift (core func $i "i32.reinterpret_f32"))
464
)
465
(func (export "f64-to-u64") (param "a" float64) (result u64)
466
(canon lift (core func $i "i64.reinterpret_f64"))
467
)
468
(func (export "u32-to-f32") (param "a" u32) (result float32)
469
(canon lift (core func $i "f32.reinterpret_i32"))
470
)
471
(func (export "u64-to-f64") (param "a" u64) (result float64)
472
(canon lift (core func $i "f64.reinterpret_i64"))
473
)
474
)
475
"#;
476
477
let engine = super::engine();
478
let component = Component::new(&engine, component)?;
479
let mut store = Store::new(&engine, ());
480
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
481
let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,)>(&mut store, "f32-to-u32")?;
482
let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,)>(&mut store, "f64-to-u64")?;
483
let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,)>(&mut store, "u32-to-f32")?;
484
let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,)>(&mut store, "u64-to-f64")?;
485
486
assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),));
487
f32_to_u32.post_return(&mut store)?;
488
assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),));
489
f64_to_u64.post_return(&mut store)?;
490
assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,));
491
u32_to_f32.post_return(&mut store)?;
492
assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,));
493
u64_to_f64.post_return(&mut store)?;
494
495
assert_eq!(
496
u32_to_f32
497
.call(&mut store, (CANON_32BIT_NAN | 1,))?
498
.0
499
.to_bits(),
500
CANON_32BIT_NAN | 1
501
);
502
u32_to_f32.post_return(&mut store)?;
503
assert_eq!(
504
u64_to_f64
505
.call(&mut store, (CANON_64BIT_NAN | 1,))?
506
.0
507
.to_bits(),
508
CANON_64BIT_NAN | 1,
509
);
510
u64_to_f64.post_return(&mut store)?;
511
512
assert_eq!(
513
f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?,
514
(CANON_32BIT_NAN | 1,)
515
);
516
f32_to_u32.post_return(&mut store)?;
517
assert_eq!(
518
f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?,
519
(CANON_64BIT_NAN | 1,)
520
);
521
f64_to_u64.post_return(&mut store)?;
522
523
Ok(())
524
}
525
526
#[test]
527
fn bools() -> Result<()> {
528
let component = r#"
529
(component
530
(core module $m
531
(func (export "pass") (param i32) (result i32) local.get 0)
532
)
533
(core instance $i (instantiate $m))
534
535
(func (export "u32-to-bool") (param "a" u32) (result bool)
536
(canon lift (core func $i "pass"))
537
)
538
(func (export "bool-to-u32") (param "a" bool) (result u32)
539
(canon lift (core func $i "pass"))
540
)
541
)
542
"#;
543
544
let engine = super::engine();
545
let component = Component::new(&engine, component)?;
546
let mut store = Store::new(&engine, ());
547
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
548
let u32_to_bool = instance.get_typed_func::<(u32,), (bool,)>(&mut store, "u32-to-bool")?;
549
let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,)>(&mut store, "bool-to-u32")?;
550
551
assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,));
552
bool_to_u32.post_return(&mut store)?;
553
assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,));
554
bool_to_u32.post_return(&mut store)?;
555
assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,));
556
u32_to_bool.post_return(&mut store)?;
557
assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,));
558
u32_to_bool.post_return(&mut store)?;
559
assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,));
560
u32_to_bool.post_return(&mut store)?;
561
562
Ok(())
563
}
564
565
#[test]
566
fn chars() -> Result<()> {
567
let component = r#"
568
(component
569
(core module $m
570
(func (export "pass") (param i32) (result i32) local.get 0)
571
)
572
(core instance $i (instantiate $m))
573
574
(func (export "u32-to-char") (param "a" u32) (result char)
575
(canon lift (core func $i "pass"))
576
)
577
(func (export "char-to-u32") (param "a" char) (result u32)
578
(canon lift (core func $i "pass"))
579
)
580
)
581
"#;
582
583
let engine = super::engine();
584
let component = Component::new(&engine, component)?;
585
let mut store = Store::new(&engine, ());
586
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
587
let u32_to_char = instance.get_typed_func::<(u32,), (char,)>(&mut store, "u32-to-char")?;
588
let char_to_u32 = instance.get_typed_func::<(char,), (u32,)>(&mut store, "char-to-u32")?;
589
590
let mut roundtrip = |x: char| -> Result<()> {
591
assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,));
592
char_to_u32.post_return(&mut store)?;
593
assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,));
594
u32_to_char.post_return(&mut store)?;
595
Ok(())
596
};
597
598
roundtrip('x')?;
599
roundtrip('a')?;
600
roundtrip('\0')?;
601
roundtrip('\n')?;
602
roundtrip('')?;
603
604
let u32_to_char = |store: &mut Store<()>| {
605
Linker::new(&engine)
606
.instantiate(&mut *store, &component)?
607
.get_typed_func::<(u32,), (char,)>(&mut *store, "u32-to-char")
608
};
609
let err = u32_to_char(&mut store)?
610
.call(&mut store, (0xd800,))
611
.unwrap_err();
612
assert!(err.to_string().contains("integer out of range"), "{}", err);
613
let err = u32_to_char(&mut store)?
614
.call(&mut store, (0xdfff,))
615
.unwrap_err();
616
assert!(err.to_string().contains("integer out of range"), "{}", err);
617
let err = u32_to_char(&mut store)?
618
.call(&mut store, (0x110000,))
619
.unwrap_err();
620
assert!(err.to_string().contains("integer out of range"), "{}", err);
621
let err = u32_to_char(&mut store)?
622
.call(&mut store, (u32::MAX,))
623
.unwrap_err();
624
assert!(err.to_string().contains("integer out of range"), "{}", err);
625
626
Ok(())
627
}
628
629
#[test]
630
fn tuple_result() -> Result<()> {
631
let component = r#"
632
(component
633
(core module $m
634
(memory (export "memory") 1)
635
(func (export "foo") (param i32 i32 f32 f64) (result i32)
636
(local $base i32)
637
(local.set $base (i32.const 8))
638
(i32.store8 offset=0 (local.get $base) (local.get 0))
639
(i32.store16 offset=2 (local.get $base) (local.get 1))
640
(f32.store offset=4 (local.get $base) (local.get 2))
641
(f64.store offset=8 (local.get $base) (local.get 3))
642
local.get $base
643
)
644
645
(func (export "invalid") (result i32)
646
i32.const -8
647
)
648
)
649
(core instance $i (instantiate $m))
650
651
(type $result (tuple s8 u16 float32 float64))
652
(func (export "tuple")
653
(param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result)
654
(canon lift (core func $i "foo") (memory $i "memory"))
655
)
656
(func (export "invalid") (result $result)
657
(canon lift (core func $i "invalid") (memory $i "memory"))
658
)
659
)
660
"#;
661
662
let engine = super::engine();
663
let component = Component::new(&engine, component)?;
664
let mut store = Store::new(&engine, ());
665
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
666
667
let input = (-1, 100, 3.0, 100.0);
668
let output = instance
669
.get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),)>(&mut store, "tuple")?
670
.call_and_post_return(&mut store, input)?;
671
assert_eq!((input,), output);
672
673
let invalid_func =
674
instance.get_typed_func::<(), ((i8, u16, f32, f64),)>(&mut store, "invalid")?;
675
let err = invalid_func.call(&mut store, ()).err().unwrap();
676
assert!(
677
err.to_string().contains("pointer out of bounds of memory"),
678
"{}",
679
err
680
);
681
682
Ok(())
683
}
684
685
#[test]
686
fn strings() -> Result<()> {
687
let component = format!(
688
r#"(component
689
(core module $m
690
(memory (export "memory") 1)
691
(func (export "roundtrip") (param i32 i32) (result i32)
692
(local $base i32)
693
(local.set $base
694
(call $realloc
695
(i32.const 0)
696
(i32.const 0)
697
(i32.const 4)
698
(i32.const 8)))
699
(i32.store offset=0
700
(local.get $base)
701
(local.get 0))
702
(i32.store offset=4
703
(local.get $base)
704
(local.get 1))
705
(local.get $base)
706
)
707
708
{REALLOC_AND_FREE}
709
)
710
(core instance $i (instantiate $m))
711
712
(func (export "list8-to-str") (param "a" (list u8)) (result string)
713
(canon lift
714
(core func $i "roundtrip")
715
(memory $i "memory")
716
(realloc (func $i "realloc"))
717
)
718
)
719
(func (export "str-to-list8") (param "a" string) (result (list u8))
720
(canon lift
721
(core func $i "roundtrip")
722
(memory $i "memory")
723
(realloc (func $i "realloc"))
724
)
725
)
726
(func (export "list16-to-str") (param "a" (list u16)) (result string)
727
(canon lift
728
(core func $i "roundtrip")
729
string-encoding=utf16
730
(memory $i "memory")
731
(realloc (func $i "realloc"))
732
)
733
)
734
(func (export "str-to-list16") (param "a" string) (result (list u16))
735
(canon lift
736
(core func $i "roundtrip")
737
string-encoding=utf16
738
(memory $i "memory")
739
(realloc (func $i "realloc"))
740
)
741
)
742
)"#
743
);
744
745
let engine = super::engine();
746
let component = Component::new(&engine, component)?;
747
let mut store = Store::new(&engine, ());
748
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
749
let list8_to_str =
750
instance.get_typed_func::<(&[u8],), (WasmStr,)>(&mut store, "list8-to-str")?;
751
let str_to_list8 =
752
instance.get_typed_func::<(&str,), (WasmList<u8>,)>(&mut store, "str-to-list8")?;
753
let list16_to_str =
754
instance.get_typed_func::<(&[u16],), (WasmStr,)>(&mut store, "list16-to-str")?;
755
let str_to_list16 =
756
instance.get_typed_func::<(&str,), (WasmList<u16>,)>(&mut store, "str-to-list16")?;
757
758
let mut roundtrip = |x: &str| -> Result<()> {
759
let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0;
760
assert_eq!(ret.to_str(&store)?, x);
761
list8_to_str.post_return(&mut store)?;
762
763
let utf16 = x.encode_utf16().collect::<Vec<_>>();
764
let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0;
765
assert_eq!(ret.to_str(&store)?, x);
766
list16_to_str.post_return(&mut store)?;
767
768
let ret = str_to_list8.call(&mut store, (x,))?.0;
769
assert_eq!(
770
ret.iter(&mut store).collect::<Result<Vec<_>>>()?,
771
x.as_bytes()
772
);
773
str_to_list8.post_return(&mut store)?;
774
775
let ret = str_to_list16.call(&mut store, (x,))?.0;
776
assert_eq!(ret.iter(&mut store).collect::<Result<Vec<_>>>()?, utf16,);
777
str_to_list16.post_return(&mut store)?;
778
779
Ok(())
780
};
781
782
roundtrip("")?;
783
roundtrip("foo")?;
784
roundtrip("hello there")?;
785
roundtrip("💝")?;
786
roundtrip("Löwe 老虎 Léopard")?;
787
788
let ret = list8_to_str.call(&mut store, (b"\xff",))?.0;
789
let err = ret.to_str(&store).unwrap_err();
790
assert!(err.to_string().contains("invalid utf-8"), "{}", err);
791
list8_to_str.post_return(&mut store)?;
792
793
let ret = list8_to_str
794
.call(&mut store, (b"hello there \xff invalid",))?
795
.0;
796
let err = ret.to_str(&store).unwrap_err();
797
assert!(err.to_string().contains("invalid utf-8"), "{}", err);
798
list8_to_str.post_return(&mut store)?;
799
800
let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0;
801
let err = ret.to_str(&store).unwrap_err();
802
assert!(err.to_string().contains("unpaired surrogate"), "{}", err);
803
list16_to_str.post_return(&mut store)?;
804
805
let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0;
806
let err = ret.to_str(&store).unwrap_err();
807
assert!(err.to_string().contains("unpaired surrogate"), "{}", err);
808
list16_to_str.post_return(&mut store)?;
809
810
let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0;
811
let err = ret.to_str(&store).unwrap_err();
812
assert!(err.to_string().contains("unpaired surrogate"), "{}", err);
813
list16_to_str.post_return(&mut store)?;
814
815
Ok(())
816
}
817
818
#[tokio::test]
819
async fn async_reentrance() -> Result<()> {
820
_ = env_logger::try_init();
821
822
let component = r#"
823
(component
824
(core module $shim
825
(import "" "task.return" (func $task-return (param i32)))
826
(table (export "funcs") 1 1 funcref)
827
(func (export "export") (param i32) (result i32)
828
(call_indirect (i32.const 0) (local.get 0))
829
)
830
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
831
)
832
(core func $task-return (canon task.return (result u32)))
833
(core instance $shim (instantiate $shim
834
(with "" (instance (export "task.return" (func $task-return))))
835
))
836
(func $shim-export (param "p1" u32) (result u32)
837
(canon lift (core func $shim "export") async (callback (func $shim "callback")))
838
)
839
840
(component $inner
841
(import "import" (func $import (param "p1" u32) (result u32)))
842
(core module $libc (memory (export "memory") 1))
843
(core instance $libc (instantiate $libc))
844
(core func $import (canon lower (func $import) async (memory $libc "memory")))
845
846
(core module $m
847
(import "libc" "memory" (memory 1))
848
(import "" "import" (func $import (param i32 i32) (result i32)))
849
(import "" "task.return" (func $task-return (param i32)))
850
(func (export "export") (param i32) (result i32)
851
(i32.store offset=0 (i32.const 1200) (local.get 0))
852
(call $import (i32.const 1200) (i32.const 1204))
853
drop
854
(call $task-return (i32.load offset=0 (i32.const 1204)))
855
i32.const 0
856
)
857
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
858
)
859
(core type $task-return-type (func (param i32)))
860
(core func $task-return (canon task.return (result u32)))
861
(core instance $i (instantiate $m
862
(with "" (instance
863
(export "task.return" (func $task-return))
864
(export "import" (func $import))
865
))
866
(with "libc" (instance $libc))
867
))
868
(func (export "export") (param "p1" u32) (result u32)
869
(canon lift (core func $i "export") async (callback (func $i "callback")))
870
)
871
)
872
(instance $inner (instantiate $inner (with "import" (func $shim-export))))
873
874
(core module $libc (memory (export "memory") 1))
875
(core instance $libc (instantiate $libc))
876
(core func $inner-export (canon lower (func $inner "export") async (memory $libc "memory")))
877
878
(core module $donut
879
(import "" "funcs" (table 1 1 funcref))
880
(import "libc" "memory" (memory 1))
881
(import "" "import" (func $import (param i32 i32) (result i32)))
882
(import "" "task.return" (func $task-return (param i32)))
883
(func $host-export (export "export") (param i32) (result i32)
884
(i32.store offset=0 (i32.const 1200) (local.get 0))
885
(call $import (i32.const 1200) (i32.const 1204))
886
drop
887
(call $task-return (i32.load offset=0 (i32.const 1204)))
888
i32.const 0
889
)
890
(func $guest-export (export "guest-export") (param i32) (result i32) unreachable)
891
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
892
(func $start
893
(table.set (i32.const 0) (ref.func $guest-export))
894
)
895
(start $start)
896
)
897
898
(core instance $donut (instantiate $donut
899
(with "" (instance
900
(export "task.return" (func $task-return))
901
(export "import" (func $inner-export))
902
(export "funcs" (table $shim "funcs"))
903
))
904
(with "libc" (instance $libc))
905
))
906
(func (export "export") (param "p1" u32) (result u32)
907
(canon lift (core func $donut "export") async (callback (func $donut "callback")))
908
)
909
)"#;
910
911
let mut config = Config::new();
912
config.wasm_component_model_async(true);
913
config.async_support(true);
914
let engine = &Engine::new(&config)?;
915
let component = Component::new(&engine, component)?;
916
let mut store = Store::new(&engine, ());
917
918
let instance = Linker::new(&engine)
919
.instantiate_async(&mut store, &component)
920
.await?;
921
let func = instance.get_typed_func::<(u32,), (u32,)>(&mut store, "export")?;
922
let message = "cannot enter component instance";
923
match instance
924
.run_concurrent(&mut store, async move |accessor| {
925
func.call_concurrent(accessor, (42,)).await
926
})
927
.await
928
{
929
Ok(_) => panic!(),
930
Err(e) => assert!(
931
format!("{e:?}").contains(message),
932
"expected `{message}`; got `{e:?}`"
933
),
934
}
935
936
Ok(())
937
}
938
939
#[tokio::test]
940
async fn missing_task_return_call_stackless() -> Result<()> {
941
task_return_trap(
942
r#"(component
943
(core module $m
944
(import "" "task.return" (func $task-return))
945
(func (export "foo") (result i32)
946
i32.const 0
947
)
948
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
949
)
950
(core func $task-return (canon task.return))
951
(core instance $i (instantiate $m
952
(with "" (instance (export "task.return" (func $task-return))))
953
))
954
(func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback"))))
955
)"#,
956
"wasm trap: async-lifted export failed to produce a result",
957
)
958
.await
959
}
960
961
#[tokio::test]
962
async fn missing_task_return_call_stackful() -> Result<()> {
963
task_return_trap(
964
r#"(component
965
(core module $m
966
(import "" "task.return" (func $task-return))
967
(func (export "foo"))
968
)
969
(core func $task-return (canon task.return))
970
(core instance $i (instantiate $m
971
(with "" (instance (export "task.return" (func $task-return))))
972
))
973
(func (export "foo") (canon lift (core func $i "foo") async))
974
)"#,
975
"wasm trap: async-lifted export failed to produce a result",
976
)
977
.await
978
}
979
980
#[tokio::test]
981
async fn task_return_type_mismatch() -> Result<()> {
982
task_return_trap(
983
r#"(component
984
(core module $m
985
(import "" "task.return" (func $task-return (param i32)))
986
(func (export "foo") (call $task-return (i32.const 42)))
987
)
988
(core func $task-return (canon task.return (result u32)))
989
(core instance $i (instantiate $m
990
(with "" (instance (export "task.return" (func $task-return))))
991
))
992
(func (export "foo") (canon lift (core func $i "foo") async))
993
)"#,
994
"invalid `task.return` signature and/or options for current task",
995
)
996
.await
997
}
998
999
#[tokio::test]
1000
async fn task_return_memory_mismatch() -> Result<()> {
1001
task_return_trap(
1002
r#"(component
1003
(core module $libc (memory (export "memory") 1))
1004
(core instance $libc (instantiate $libc))
1005
(core module $m
1006
(import "" "task.return" (func $task-return))
1007
(func (export "foo") (call $task-return))
1008
)
1009
(core func $task-return (canon task.return (memory $libc "memory")))
1010
(core instance $i (instantiate $m
1011
(with "" (instance (export "task.return" (func $task-return))))
1012
))
1013
(func (export "foo") (canon lift (core func $i "foo") async))
1014
)"#,
1015
"invalid `task.return` signature and/or options for current task",
1016
)
1017
.await
1018
}
1019
1020
#[tokio::test]
1021
async fn task_return_string_encoding_mismatch() -> Result<()> {
1022
task_return_trap(
1023
r#"(component
1024
(core module $m
1025
(import "" "task.return" (func $task-return))
1026
(func (export "foo") (call $task-return))
1027
)
1028
(core func $task-return (canon task.return string-encoding=utf16))
1029
(core instance $i (instantiate $m
1030
(with "" (instance (export "task.return" (func $task-return))))
1031
))
1032
(func (export "foo") (canon lift (core func $i "foo") async))
1033
)"#,
1034
"invalid `task.return` signature and/or options for current task",
1035
)
1036
.await
1037
}
1038
1039
async fn task_return_trap(component: &str, substring: &str) -> Result<()> {
1040
let mut config = Config::new();
1041
config.wasm_component_model_async(true);
1042
config.wasm_component_model_async_stackful(true);
1043
config.async_support(true);
1044
let engine = &Engine::new(&config)?;
1045
let component = Component::new(&engine, component)?;
1046
let mut store = Store::new(&engine, ());
1047
1048
let instance = Linker::new(&engine)
1049
.instantiate_async(&mut store, &component)
1050
.await?;
1051
1052
let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?;
1053
match instance
1054
.run_concurrent(&mut store, async move |accessor| {
1055
func.call_concurrent(accessor, ()).await
1056
})
1057
.await
1058
{
1059
Ok(_) => panic!(),
1060
Err(e) => {
1061
assert!(
1062
format!("{e:?}").contains(substring),
1063
"could not find `{substring}` in `{e:?}`"
1064
)
1065
}
1066
}
1067
1068
Ok(())
1069
}
1070
1071
#[tokio::test]
1072
async fn many_parameters() -> Result<()> {
1073
test_many_parameters(false, false).await
1074
}
1075
1076
#[tokio::test]
1077
async fn many_parameters_concurrent() -> Result<()> {
1078
test_many_parameters(false, true).await
1079
}
1080
1081
#[tokio::test]
1082
async fn many_parameters_dynamic() -> Result<()> {
1083
test_many_parameters(true, false).await
1084
}
1085
1086
#[tokio::test]
1087
async fn many_parameters_dynamic_concurrent() -> Result<()> {
1088
test_many_parameters(true, true).await
1089
}
1090
1091
async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> {
1092
let (body, async_opts) = if concurrent {
1093
(
1094
r#"
1095
(call $task-return
1096
(i32.const 0)
1097
(i32.mul
1098
(memory.size)
1099
(i32.const 65536)
1100
)
1101
(local.get 0)
1102
)
1103
1104
(i32.const 0)
1105
"#,
1106
r#"async (callback (func $i "callback"))"#,
1107
)
1108
} else {
1109
(
1110
r#"
1111
(local $base i32)
1112
1113
;; Allocate space for the return
1114
(local.set $base
1115
(call $realloc
1116
(i32.const 0)
1117
(i32.const 0)
1118
(i32.const 4)
1119
(i32.const 12)))
1120
1121
;; Store the pointer/length of the entire linear memory
1122
;; so we have access to everything.
1123
(i32.store offset=0
1124
(local.get $base)
1125
(i32.const 0))
1126
(i32.store offset=4
1127
(local.get $base)
1128
(i32.mul
1129
(memory.size)
1130
(i32.const 65536)))
1131
1132
;; And also store our pointer parameter
1133
(i32.store offset=8
1134
(local.get $base)
1135
(local.get 0))
1136
1137
(local.get $base)
1138
"#,
1139
"",
1140
)
1141
};
1142
1143
let component = format!(
1144
r#"(component
1145
(core module $libc
1146
(memory (export "memory") 1)
1147
1148
{REALLOC_AND_FREE}
1149
)
1150
(core instance $libc (instantiate $libc))
1151
(core module $m
1152
(import "libc" "memory" (memory 1))
1153
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
1154
(import "" "task.return" (func $task-return (param i32 i32 i32)))
1155
(func (export "foo") (param i32) (result i32)
1156
{body}
1157
)
1158
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
1159
)
1160
(type $tuple (tuple (list u8) u32))
1161
(core func $task-return (canon task.return
1162
(result $tuple)
1163
(memory $libc "memory")
1164
))
1165
(core instance $i (instantiate $m
1166
(with "" (instance (export "task.return" (func $task-return))))
1167
(with "libc" (instance $libc))
1168
))
1169
1170
(type $t (func
1171
(param "p1" s8) ;; offset 0, size 1
1172
(param "p2" u64) ;; offset 8, size 8
1173
(param "p3" float32) ;; offset 16, size 4
1174
(param "p4" u8) ;; offset 20, size 1
1175
(param "p5" s16) ;; offset 22, size 2
1176
(param "p6" string) ;; offset 24, size 8
1177
(param "p7" (list u32)) ;; offset 32, size 8
1178
(param "p8" bool) ;; offset 40, size 1
1179
(param "p9" bool) ;; offset 41, size 1
1180
(param "p0" char) ;; offset 44, size 4
1181
(param "pa" (list bool)) ;; offset 48, size 8
1182
(param "pb" (list char)) ;; offset 56, size 8
1183
(param "pc" (list string)) ;; offset 64, size 8
1184
1185
(result $tuple)
1186
))
1187
(func (export "many-param") (type $t)
1188
(canon lift
1189
(core func $i "foo")
1190
(memory $libc "memory")
1191
(realloc (func $libc "realloc"))
1192
{async_opts}
1193
)
1194
)
1195
)"#
1196
);
1197
1198
let mut config = Config::new();
1199
config.wasm_component_model_async(true);
1200
config.async_support(true);
1201
let engine = &Engine::new(&config)?;
1202
let component = Component::new(&engine, component)?;
1203
let mut store = Store::new(&engine, ());
1204
1205
let instance = Linker::new(&engine)
1206
.instantiate_async(&mut store, &component)
1207
.await?;
1208
1209
let input = (
1210
-100,
1211
u64::MAX / 2,
1212
f32::from_bits(CANON_32BIT_NAN | 1),
1213
38,
1214
18831,
1215
"this is the first string",
1216
[1, 2, 3, 4, 5, 6, 7, 8].as_slice(),
1217
true,
1218
false,
1219
'',
1220
[false, true, false, true, true].as_slice(),
1221
['', '', '', '', ''].as_slice(),
1222
[
1223
"the quick",
1224
"brown fox",
1225
"was too lazy",
1226
"to jump over the dog",
1227
"what a demanding dog",
1228
]
1229
.as_slice(),
1230
);
1231
1232
let (memory, pointer) = if dynamic {
1233
let input = vec![
1234
Val::S8(input.0),
1235
Val::U64(input.1),
1236
Val::Float32(input.2),
1237
Val::U8(input.3),
1238
Val::S16(input.4),
1239
Val::String(input.5.into()),
1240
Val::List(input.6.iter().copied().map(Val::U32).collect()),
1241
Val::Bool(input.7),
1242
Val::Bool(input.8),
1243
Val::Char(input.9),
1244
Val::List(input.10.iter().copied().map(Val::Bool).collect()),
1245
Val::List(input.11.iter().copied().map(Val::Char).collect()),
1246
Val::List(input.12.iter().map(|&s| Val::String(s.into())).collect()),
1247
];
1248
let func = instance.get_func(&mut store, "many-param").unwrap();
1249
1250
let mut results = vec![Val::Bool(false)];
1251
if concurrent {
1252
instance
1253
.run_concurrent(&mut store, async |store| {
1254
func.call_concurrent(store, &input, &mut results).await
1255
})
1256
.await??;
1257
} else {
1258
func.call_async(&mut store, &input, &mut results).await?;
1259
};
1260
let mut results = results.into_iter();
1261
let Some(Val::Tuple(results)) = results.next() else {
1262
panic!()
1263
};
1264
let mut results = results.into_iter();
1265
let Some(Val::List(memory)) = results.next() else {
1266
panic!()
1267
};
1268
let Some(Val::U32(pointer)) = results.next() else {
1269
panic!()
1270
};
1271
(
1272
memory
1273
.into_iter()
1274
.map(|v| if let Val::U8(v) = v { v } else { panic!() })
1275
.collect(),
1276
pointer,
1277
)
1278
} else {
1279
let func = instance.get_typed_func::<(
1280
i8,
1281
u64,
1282
f32,
1283
u8,
1284
i16,
1285
&str,
1286
&[u32],
1287
bool,
1288
bool,
1289
char,
1290
&[bool],
1291
&[char],
1292
&[&str],
1293
), ((Vec<u8>, u32),)>(&mut store, "many-param")?;
1294
1295
if concurrent {
1296
instance
1297
.run_concurrent(&mut store, async move |accessor| {
1298
func.call_concurrent(accessor, input).await
1299
})
1300
.await??
1301
.0
1302
} else {
1303
func.call_async(&mut store, input).await?.0
1304
}
1305
};
1306
let memory = &memory[..];
1307
1308
let mut actual = &memory[pointer as usize..][..72];
1309
assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0);
1310
actual.skip::<7>();
1311
assert_eq!(u64::from_le_bytes(*actual.take_n::<8>()), input.1);
1312
assert_eq!(
1313
u32::from_le_bytes(*actual.take_n::<4>()),
1314
CANON_32BIT_NAN | 1
1315
);
1316
assert_eq!(u8::from_le_bytes(*actual.take_n::<1>()), input.3);
1317
actual.skip::<1>();
1318
assert_eq!(i16::from_le_bytes(*actual.take_n::<2>()), input.4);
1319
assert_eq!(actual.ptr_len(memory, 1), input.5.as_bytes());
1320
let mut mem = actual.ptr_len(memory, 4);
1321
for expected in input.6.iter() {
1322
assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected);
1323
}
1324
assert!(mem.is_empty());
1325
assert_eq!(actual.take_n::<1>(), &[input.7 as u8]);
1326
assert_eq!(actual.take_n::<1>(), &[input.8 as u8]);
1327
actual.skip::<2>();
1328
assert_eq!(u32::from_le_bytes(*actual.take_n::<4>()), input.9 as u32);
1329
1330
// (list bool)
1331
mem = actual.ptr_len(memory, 1);
1332
for expected in input.10.iter() {
1333
assert_eq!(mem.take_n::<1>(), &[*expected as u8]);
1334
}
1335
assert!(mem.is_empty());
1336
1337
// (list char)
1338
mem = actual.ptr_len(memory, 4);
1339
for expected in input.11.iter() {
1340
assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected as u32);
1341
}
1342
assert!(mem.is_empty());
1343
1344
// (list string)
1345
mem = actual.ptr_len(memory, 8);
1346
for expected in input.12.iter() {
1347
let actual = mem.ptr_len(memory, 1);
1348
assert_eq!(actual, expected.as_bytes());
1349
}
1350
assert!(mem.is_empty());
1351
assert!(actual.is_empty());
1352
1353
Ok(())
1354
}
1355
1356
#[tokio::test]
1357
async fn many_results() -> Result<()> {
1358
test_many_results(false, false).await
1359
}
1360
1361
#[tokio::test]
1362
async fn many_results_concurrent() -> Result<()> {
1363
test_many_results(false, true).await
1364
}
1365
1366
#[tokio::test]
1367
async fn many_results_dynamic() -> Result<()> {
1368
test_many_results(true, false).await
1369
}
1370
1371
#[tokio::test]
1372
async fn many_results_dynamic_concurrent() -> Result<()> {
1373
test_many_results(true, true).await
1374
}
1375
1376
async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> {
1377
let (ret, async_opts) = if concurrent {
1378
(
1379
r#"
1380
call $task-return
1381
i32.const 0
1382
"#,
1383
r#"async (callback (func $i "callback"))"#,
1384
)
1385
} else {
1386
("", "")
1387
};
1388
1389
let my_nan = CANON_32BIT_NAN | 1;
1390
1391
let component = format!(
1392
r#"(component
1393
(core module $libc
1394
(memory (export "memory") 1)
1395
1396
{REALLOC_AND_FREE}
1397
)
1398
(core instance $libc (instantiate $libc))
1399
(core module $m
1400
(import "libc" "memory" (memory 1))
1401
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
1402
(import "" "task.return" (func $task-return (param i32)))
1403
(func (export "foo") (result i32)
1404
(local $base i32)
1405
(local $string i32)
1406
(local $list i32)
1407
1408
(local.set $base
1409
(call $realloc
1410
(i32.const 0)
1411
(i32.const 0)
1412
(i32.const 8)
1413
(i32.const 72)))
1414
1415
(i32.store8 offset=0
1416
(local.get $base)
1417
(i32.const -100))
1418
1419
(i64.store offset=8
1420
(local.get $base)
1421
(i64.const 9223372036854775807))
1422
1423
(f32.store offset=16
1424
(local.get $base)
1425
(f32.reinterpret_i32 (i32.const {my_nan})))
1426
1427
(i32.store8 offset=20
1428
(local.get $base)
1429
(i32.const 38))
1430
1431
(i32.store16 offset=22
1432
(local.get $base)
1433
(i32.const 18831))
1434
1435
(local.set $string
1436
(call $realloc
1437
(i32.const 0)
1438
(i32.const 0)
1439
(i32.const 1)
1440
(i32.const 6)))
1441
1442
(i32.store8 offset=0
1443
(local.get $string)
1444
(i32.const 97)) ;; 'a'
1445
(i32.store8 offset=1
1446
(local.get $string)
1447
(i32.const 98)) ;; 'b'
1448
(i32.store8 offset=2
1449
(local.get $string)
1450
(i32.const 99)) ;; 'c'
1451
(i32.store8 offset=3
1452
(local.get $string)
1453
(i32.const 100)) ;; 'd'
1454
(i32.store8 offset=4
1455
(local.get $string)
1456
(i32.const 101)) ;; 'e'
1457
(i32.store8 offset=5
1458
(local.get $string)
1459
(i32.const 102)) ;; 'f'
1460
1461
(i32.store offset=24
1462
(local.get $base)
1463
(local.get $string))
1464
1465
(i32.store offset=28
1466
(local.get $base)
1467
(i32.const 2))
1468
1469
(local.set $list
1470
(call $realloc
1471
(i32.const 0)
1472
(i32.const 0)
1473
(i32.const 4)
1474
(i32.const 32)))
1475
1476
(i32.store offset=0
1477
(local.get $list)
1478
(i32.const 1))
1479
(i32.store offset=4
1480
(local.get $list)
1481
(i32.const 2))
1482
(i32.store offset=8
1483
(local.get $list)
1484
(i32.const 3))
1485
(i32.store offset=12
1486
(local.get $list)
1487
(i32.const 4))
1488
(i32.store offset=16
1489
(local.get $list)
1490
(i32.const 5))
1491
(i32.store offset=20
1492
(local.get $list)
1493
(i32.const 6))
1494
(i32.store offset=24
1495
(local.get $list)
1496
(i32.const 7))
1497
(i32.store offset=28
1498
(local.get $list)
1499
(i32.const 8))
1500
1501
(i32.store offset=32
1502
(local.get $base)
1503
(local.get $list))
1504
1505
(i32.store offset=36
1506
(local.get $base)
1507
(i32.const 8))
1508
1509
(i32.store8 offset=40
1510
(local.get $base)
1511
(i32.const 1))
1512
1513
(i32.store8 offset=41
1514
(local.get $base)
1515
(i32.const 0))
1516
1517
(i32.store offset=44
1518
(local.get $base)
1519
(i32.const 128681)) ;; '🚩'
1520
1521
(local.set $list
1522
(call $realloc
1523
(i32.const 0)
1524
(i32.const 0)
1525
(i32.const 1)
1526
(i32.const 5)))
1527
1528
(i32.store8 offset=0
1529
(local.get $list)
1530
(i32.const 0))
1531
(i32.store8 offset=1
1532
(local.get $list)
1533
(i32.const 1))
1534
(i32.store8 offset=2
1535
(local.get $list)
1536
(i32.const 0))
1537
(i32.store8 offset=3
1538
(local.get $list)
1539
(i32.const 1))
1540
(i32.store8 offset=4
1541
(local.get $list)
1542
(i32.const 1))
1543
1544
(i32.store offset=48
1545
(local.get $base)
1546
(local.get $list))
1547
1548
(i32.store offset=52
1549
(local.get $base)
1550
(i32.const 5))
1551
1552
(local.set $list
1553
(call $realloc
1554
(i32.const 0)
1555
(i32.const 0)
1556
(i32.const 4)
1557
(i32.const 20)))
1558
1559
(i32.store offset=0
1560
(local.get $list)
1561
(i32.const 127820)) ;; '🍌'
1562
(i32.store offset=4
1563
(local.get $list)
1564
(i32.const 129360)) ;; '🥐'
1565
(i32.store offset=8
1566
(local.get $list)
1567
(i32.const 127831)) ;; '🍗'
1568
(i32.store offset=12
1569
(local.get $list)
1570
(i32.const 127833)) ;; '🍙'
1571
(i32.store offset=16
1572
(local.get $list)
1573
(i32.const 127841)) ;; '🍡'
1574
1575
(i32.store offset=56
1576
(local.get $base)
1577
(local.get $list))
1578
1579
(i32.store offset=60
1580
(local.get $base)
1581
(i32.const 5))
1582
1583
(local.set $list
1584
(call $realloc
1585
(i32.const 0)
1586
(i32.const 0)
1587
(i32.const 4)
1588
(i32.const 16)))
1589
1590
(i32.store offset=0
1591
(local.get $list)
1592
(i32.add (local.get $string) (i32.const 2)))
1593
(i32.store offset=4
1594
(local.get $list)
1595
(i32.const 2))
1596
(i32.store offset=8
1597
(local.get $list)
1598
(i32.add (local.get $string) (i32.const 4)))
1599
(i32.store offset=12
1600
(local.get $list)
1601
(i32.const 2))
1602
1603
(i32.store offset=64
1604
(local.get $base)
1605
(local.get $list))
1606
1607
(i32.store offset=68
1608
(local.get $base)
1609
(i32.const 2))
1610
1611
local.get $base
1612
1613
{ret}
1614
)
1615
(func (export "callback") (param i32 i32 i32) (result i32) unreachable)
1616
)
1617
(type $tuple (tuple
1618
s8
1619
u64
1620
float32
1621
u8
1622
s16
1623
string
1624
(list u32)
1625
bool
1626
bool
1627
char
1628
(list bool)
1629
(list char)
1630
(list string)
1631
))
1632
(core func $task-return (canon task.return
1633
(result $tuple)
1634
(memory $libc "memory")
1635
))
1636
(core instance $i (instantiate $m
1637
(with "" (instance (export "task.return" (func $task-return))))
1638
(with "libc" (instance $libc))
1639
))
1640
1641
(type $t (func (result $tuple)))
1642
(func (export "many-results") (type $t)
1643
(canon lift
1644
(core func $i "foo")
1645
(memory $libc "memory")
1646
(realloc (func $libc "realloc"))
1647
{async_opts}
1648
)
1649
)
1650
)"#
1651
);
1652
1653
let mut config = Config::new();
1654
config.wasm_component_model_async(true);
1655
config.async_support(true);
1656
let engine = &Engine::new(&config)?;
1657
let component = Component::new(&engine, component)?;
1658
let mut store = Store::new(&engine, ());
1659
1660
let instance = Linker::new(&engine)
1661
.instantiate_async(&mut store, &component)
1662
.await?;
1663
1664
let expected = (
1665
-100i8,
1666
u64::MAX / 2,
1667
f32::from_bits(CANON_32BIT_NAN | 1),
1668
38u8,
1669
18831i16,
1670
"ab".to_string(),
1671
vec![1u32, 2, 3, 4, 5, 6, 7, 8],
1672
true,
1673
false,
1674
'',
1675
vec![false, true, false, true, true],
1676
vec!['', '', '', '', ''],
1677
vec!["cd".to_string(), "ef".to_string()],
1678
);
1679
1680
let actual = if dynamic {
1681
let func = instance.get_func(&mut store, "many-results").unwrap();
1682
1683
let mut results = vec![Val::Bool(false)];
1684
if concurrent {
1685
instance
1686
.run_concurrent(&mut store, async |store| {
1687
func.call_concurrent(store, &[], &mut results).await
1688
})
1689
.await??;
1690
} else {
1691
func.call_async(&mut store, &[], &mut results).await?;
1692
};
1693
let mut results = results.into_iter();
1694
1695
let Some(Val::Tuple(results)) = results.next() else {
1696
panic!()
1697
};
1698
let mut results = results.into_iter();
1699
let Some(Val::S8(p1)) = results.next() else {
1700
panic!()
1701
};
1702
let Some(Val::U64(p2)) = results.next() else {
1703
panic!()
1704
};
1705
let Some(Val::Float32(p3)) = results.next() else {
1706
panic!()
1707
};
1708
let Some(Val::U8(p4)) = results.next() else {
1709
panic!()
1710
};
1711
let Some(Val::S16(p5)) = results.next() else {
1712
panic!()
1713
};
1714
let Some(Val::String(p6)) = results.next() else {
1715
panic!()
1716
};
1717
let Some(Val::List(p7)) = results.next() else {
1718
panic!()
1719
};
1720
let p7 = p7
1721
.into_iter()
1722
.map(|v| if let Val::U32(v) = v { v } else { panic!() })
1723
.collect();
1724
let Some(Val::Bool(p8)) = results.next() else {
1725
panic!()
1726
};
1727
let Some(Val::Bool(p9)) = results.next() else {
1728
panic!()
1729
};
1730
let Some(Val::Char(p0)) = results.next() else {
1731
panic!()
1732
};
1733
let Some(Val::List(pa)) = results.next() else {
1734
panic!()
1735
};
1736
let pa = pa
1737
.into_iter()
1738
.map(|v| if let Val::Bool(v) = v { v } else { panic!() })
1739
.collect();
1740
let Some(Val::List(pb)) = results.next() else {
1741
panic!()
1742
};
1743
let pb = pb
1744
.into_iter()
1745
.map(|v| if let Val::Char(v) = v { v } else { panic!() })
1746
.collect();
1747
let Some(Val::List(pc)) = results.next() else {
1748
panic!()
1749
};
1750
let pc = pc
1751
.into_iter()
1752
.map(|v| if let Val::String(v) = v { v } else { panic!() })
1753
.collect();
1754
1755
(p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, pa, pb, pc)
1756
} else {
1757
let func = instance.get_typed_func::<(), ((
1758
i8,
1759
u64,
1760
f32,
1761
u8,
1762
i16,
1763
String,
1764
Vec<u32>,
1765
bool,
1766
bool,
1767
char,
1768
Vec<bool>,
1769
Vec<char>,
1770
Vec<String>,
1771
),)>(&mut store, "many-results")?;
1772
1773
if concurrent {
1774
instance
1775
.run_concurrent(&mut store, async move |accessor| {
1776
func.call_concurrent(accessor, ()).await
1777
})
1778
.await??
1779
.0
1780
} else {
1781
func.call_async(&mut store, ()).await?.0
1782
}
1783
};
1784
1785
assert_eq!(expected.0, actual.0);
1786
assert_eq!(expected.1, actual.1);
1787
assert!(expected.2.is_nan());
1788
assert!(actual.2.is_nan());
1789
assert_eq!(expected.3, actual.3);
1790
assert_eq!(expected.4, actual.4);
1791
assert_eq!(expected.5, actual.5);
1792
assert_eq!(expected.6, actual.6);
1793
assert_eq!(expected.7, actual.7);
1794
assert_eq!(expected.8, actual.8);
1795
assert_eq!(expected.9, actual.9);
1796
assert_eq!(expected.10, actual.10);
1797
assert_eq!(expected.11, actual.11);
1798
assert_eq!(expected.12, actual.12);
1799
1800
Ok(())
1801
}
1802
1803
#[test]
1804
fn some_traps() -> Result<()> {
1805
let middle_of_memory = (i32::MAX / 2) & (!0xff);
1806
let component = format!(
1807
r#"(component
1808
(core module $m
1809
(memory (export "memory") 1)
1810
(func (export "take-many") (param i32))
1811
(func (export "take-list") (param i32 i32))
1812
1813
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
1814
unreachable)
1815
)
1816
(core instance $i (instantiate $m))
1817
1818
(func (export "take-list-unreachable") (param "a" (list u8))
1819
(canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc")))
1820
)
1821
(func (export "take-string-unreachable") (param "a" string)
1822
(canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc")))
1823
)
1824
1825
(type $t (func
1826
(param "s1" string)
1827
(param "s2" string)
1828
(param "s3" string)
1829
(param "s4" string)
1830
(param "s5" string)
1831
(param "s6" string)
1832
(param "s7" string)
1833
(param "s8" string)
1834
(param "s9" string)
1835
(param "s10" string)
1836
))
1837
(func (export "take-many-unreachable") (type $t)
1838
(canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc")))
1839
)
1840
1841
(core module $m2
1842
(memory (export "memory") 1)
1843
(func (export "take-many") (param i32))
1844
(func (export "take-list") (param i32 i32))
1845
1846
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
1847
i32.const {middle_of_memory})
1848
)
1849
(core instance $i2 (instantiate $m2))
1850
1851
(func (export "take-list-base-oob") (param "a" (list u8))
1852
(canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc")))
1853
)
1854
(func (export "take-string-base-oob") (param "a" string)
1855
(canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc")))
1856
)
1857
(func (export "take-many-base-oob") (type $t)
1858
(canon lift (core func $i2 "take-many") (memory $i2 "memory") (realloc (func $i2 "realloc")))
1859
)
1860
1861
(core module $m3
1862
(memory (export "memory") 1)
1863
(func (export "take-many") (param i32))
1864
(func (export "take-list") (param i32 i32))
1865
1866
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
1867
i32.const 65532)
1868
)
1869
(core instance $i3 (instantiate $m3))
1870
1871
(func (export "take-list-end-oob") (param "a" (list u8))
1872
(canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc")))
1873
)
1874
(func (export "take-string-end-oob") (param "a" string)
1875
(canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc")))
1876
)
1877
(func (export "take-many-end-oob") (type $t)
1878
(canon lift (core func $i3 "take-many") (memory $i3 "memory") (realloc (func $i3 "realloc")))
1879
)
1880
1881
(core module $m4
1882
(memory (export "memory") 1)
1883
(func (export "take-many") (param i32))
1884
1885
(global $cnt (mut i32) (i32.const 0))
1886
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
1887
global.get $cnt
1888
if (result i32)
1889
i32.const 100000
1890
else
1891
i32.const 1
1892
global.set $cnt
1893
i32.const 0
1894
end
1895
)
1896
)
1897
(core instance $i4 (instantiate $m4))
1898
1899
(func (export "take-many-second-oob") (type $t)
1900
(canon lift (core func $i4 "take-many") (memory $i4 "memory") (realloc (func $i4 "realloc")))
1901
)
1902
)"#
1903
);
1904
1905
let engine = super::engine();
1906
let component = Component::new(&engine, component)?;
1907
let mut store = Store::new(&engine, ());
1908
let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component);
1909
1910
// This should fail when calling the allocator function for the argument
1911
let err = instance(&mut store)?
1912
.get_typed_func::<(&[u8],), ()>(&mut store, "take-list-unreachable")?
1913
.call(&mut store, (&[],))
1914
.unwrap_err()
1915
.downcast::<Trap>()?;
1916
assert_eq!(err, Trap::UnreachableCodeReached);
1917
1918
// This should fail when calling the allocator function for the argument
1919
let err = instance(&mut store)?
1920
.get_typed_func::<(&str,), ()>(&mut store, "take-string-unreachable")?
1921
.call(&mut store, ("",))
1922
.unwrap_err()
1923
.downcast::<Trap>()?;
1924
assert_eq!(err, Trap::UnreachableCodeReached);
1925
1926
// This should fail when calling the allocator function for the space
1927
// to store the arguments (before arguments are even lowered)
1928
let err = instance(&mut store)?
1929
.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
1930
&mut store,
1931
"take-many-unreachable",
1932
)?
1933
.call(&mut store, ("", "", "", "", "", "", "", "", "", ""))
1934
.unwrap_err()
1935
.downcast::<Trap>()?;
1936
assert_eq!(err, Trap::UnreachableCodeReached);
1937
1938
// Assert that when the base pointer returned by malloc is out of bounds
1939
// that errors are reported as such. Both empty and lists with contents
1940
// should all be invalid here.
1941
//
1942
// FIXME(WebAssembly/component-model#32) confirm the semantics here are
1943
// what's desired.
1944
#[track_caller]
1945
fn assert_oob(err: &anyhow::Error) {
1946
assert!(
1947
err.to_string()
1948
.contains("realloc return: beyond end of memory"),
1949
"{err:?}",
1950
);
1951
}
1952
let err = instance(&mut store)?
1953
.get_typed_func::<(&[u8],), ()>(&mut store, "take-list-base-oob")?
1954
.call(&mut store, (&[],))
1955
.unwrap_err();
1956
assert_oob(&err);
1957
let err = instance(&mut store)?
1958
.get_typed_func::<(&[u8],), ()>(&mut store, "take-list-base-oob")?
1959
.call(&mut store, (&[1],))
1960
.unwrap_err();
1961
assert_oob(&err);
1962
let err = instance(&mut store)?
1963
.get_typed_func::<(&str,), ()>(&mut store, "take-string-base-oob")?
1964
.call(&mut store, ("",))
1965
.unwrap_err();
1966
assert_oob(&err);
1967
let err = instance(&mut store)?
1968
.get_typed_func::<(&str,), ()>(&mut store, "take-string-base-oob")?
1969
.call(&mut store, ("x",))
1970
.unwrap_err();
1971
assert_oob(&err);
1972
let err = instance(&mut store)?
1973
.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
1974
&mut store,
1975
"take-many-base-oob",
1976
)?
1977
.call(&mut store, ("", "", "", "", "", "", "", "", "", ""))
1978
.unwrap_err();
1979
assert_oob(&err);
1980
1981
// Test here that when the returned pointer from malloc is one byte from the
1982
// end of memory that empty things are fine, but larger things are not.
1983
1984
instance(&mut store)?
1985
.get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")?
1986
.call_and_post_return(&mut store, (&[],))?;
1987
instance(&mut store)?
1988
.get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")?
1989
.call_and_post_return(&mut store, (&[1, 2, 3, 4],))?;
1990
let err = instance(&mut store)?
1991
.get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")?
1992
.call(&mut store, (&[1, 2, 3, 4, 5],))
1993
.unwrap_err();
1994
assert_oob(&err);
1995
instance(&mut store)?
1996
.get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")?
1997
.call_and_post_return(&mut store, ("",))?;
1998
instance(&mut store)?
1999
.get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")?
2000
.call_and_post_return(&mut store, ("abcd",))?;
2001
let err = instance(&mut store)?
2002
.get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")?
2003
.call(&mut store, ("abcde",))
2004
.unwrap_err();
2005
assert_oob(&err);
2006
let err = instance(&mut store)?
2007
.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2008
&mut store,
2009
"take-many-end-oob",
2010
)?
2011
.call(&mut store, ("", "", "", "", "", "", "", "", "", ""))
2012
.unwrap_err();
2013
assert_oob(&err);
2014
2015
// For this function the first allocation, the space to store all the
2016
// arguments, is in-bounds but then all further allocations, such as for
2017
// each individual string, are all out of bounds.
2018
let err = instance(&mut store)?
2019
.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2020
&mut store,
2021
"take-many-second-oob",
2022
)?
2023
.call(&mut store, ("", "", "", "", "", "", "", "", "", ""))
2024
.unwrap_err();
2025
assert_oob(&err);
2026
let err = instance(&mut store)?
2027
.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2028
&mut store,
2029
"take-many-second-oob",
2030
)?
2031
.call(&mut store, ("", "", "", "", "", "", "", "", "", "x"))
2032
.unwrap_err();
2033
assert_oob(&err);
2034
Ok(())
2035
}
2036
2037
#[test]
2038
fn char_bool_memory() -> Result<()> {
2039
let component = format!(
2040
r#"(component
2041
(core module $m
2042
(memory (export "memory") 1)
2043
(func (export "ret-tuple") (param i32 i32) (result i32)
2044
(local $base i32)
2045
2046
;; Allocate space for the return
2047
(local.set $base
2048
(call $realloc
2049
(i32.const 0)
2050
(i32.const 0)
2051
(i32.const 4)
2052
(i32.const 8)))
2053
2054
;; store the boolean
2055
(i32.store offset=0
2056
(local.get $base)
2057
(local.get 0))
2058
2059
;; store the char
2060
(i32.store offset=4
2061
(local.get $base)
2062
(local.get 1))
2063
2064
(local.get $base)
2065
)
2066
2067
{REALLOC_AND_FREE}
2068
)
2069
(core instance $i (instantiate $m))
2070
2071
(func (export "ret-tuple") (param "a" u32) (param "b" u32) (result (tuple bool char))
2072
(canon lift (core func $i "ret-tuple")
2073
(memory $i "memory")
2074
(realloc (func $i "realloc")))
2075
)
2076
)"#
2077
);
2078
2079
let engine = super::engine();
2080
let component = Component::new(&engine, component)?;
2081
let mut store = Store::new(&engine, ());
2082
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2083
let func = instance.get_typed_func::<(u32, u32), ((bool, char),)>(&mut store, "ret-tuple")?;
2084
2085
let (ret,) = func.call(&mut store, (0, 'a' as u32))?;
2086
assert_eq!(ret, (false, 'a'));
2087
func.post_return(&mut store)?;
2088
2089
let (ret,) = func.call(&mut store, (1, '' as u32))?;
2090
assert_eq!(ret, (true, ''));
2091
func.post_return(&mut store)?;
2092
2093
let (ret,) = func.call(&mut store, (2, 'a' as u32))?;
2094
assert_eq!(ret, (true, 'a'));
2095
func.post_return(&mut store)?;
2096
2097
assert!(func.call(&mut store, (0, 0xd800)).is_err());
2098
2099
Ok(())
2100
}
2101
2102
#[test]
2103
fn string_list_oob() -> Result<()> {
2104
let component = format!(
2105
r#"(component
2106
(core module $m
2107
(memory (export "memory") 1)
2108
(func (export "ret-list") (result i32)
2109
(local $base i32)
2110
2111
;; Allocate space for the return
2112
(local.set $base
2113
(call $realloc
2114
(i32.const 0)
2115
(i32.const 0)
2116
(i32.const 4)
2117
(i32.const 8)))
2118
2119
(i32.store offset=0
2120
(local.get $base)
2121
(i32.const 100000))
2122
(i32.store offset=4
2123
(local.get $base)
2124
(i32.const 1))
2125
2126
(local.get $base)
2127
)
2128
2129
{REALLOC_AND_FREE}
2130
)
2131
(core instance $i (instantiate $m))
2132
2133
(func (export "ret-list-u8") (result (list u8))
2134
(canon lift (core func $i "ret-list")
2135
(memory $i "memory")
2136
(realloc (func $i "realloc"))
2137
)
2138
)
2139
(func (export "ret-string") (result string)
2140
(canon lift (core func $i "ret-list")
2141
(memory $i "memory")
2142
(realloc (func $i "realloc"))
2143
)
2144
)
2145
)"#
2146
);
2147
2148
let engine = super::engine();
2149
let component = Component::new(&engine, component)?;
2150
let mut store = Store::new(&engine, ());
2151
let ret_list_u8 = Linker::new(&engine)
2152
.instantiate(&mut store, &component)?
2153
.get_typed_func::<(), (WasmList<u8>,)>(&mut store, "ret-list-u8")?;
2154
let ret_string = Linker::new(&engine)
2155
.instantiate(&mut store, &component)?
2156
.get_typed_func::<(), (WasmStr,)>(&mut store, "ret-string")?;
2157
2158
let err = ret_list_u8.call(&mut store, ()).err().unwrap();
2159
assert!(err.to_string().contains("out of bounds"), "{}", err);
2160
2161
let err = ret_string.call(&mut store, ()).err().unwrap();
2162
assert!(err.to_string().contains("out of bounds"), "{}", err);
2163
2164
Ok(())
2165
}
2166
2167
#[test]
2168
fn tuples() -> Result<()> {
2169
let component = format!(
2170
r#"(component
2171
(core module $m
2172
(memory (export "memory") 1)
2173
(func (export "foo")
2174
(param i32 f64 i32)
2175
(result i32)
2176
2177
local.get 0
2178
i32.const 0
2179
i32.ne
2180
if unreachable end
2181
2182
local.get 1
2183
f64.const 1
2184
f64.ne
2185
if unreachable end
2186
2187
local.get 2
2188
i32.const 2
2189
i32.ne
2190
if unreachable end
2191
2192
i32.const 3
2193
)
2194
)
2195
(core instance $i (instantiate $m))
2196
2197
(func (export "foo")
2198
(param "a" (tuple s32 float64))
2199
(param "b" (tuple s8))
2200
(result (tuple u16))
2201
(canon lift (core func $i "foo"))
2202
)
2203
)"#
2204
);
2205
2206
let engine = super::engine();
2207
let component = Component::new(&engine, component)?;
2208
let mut store = Store::new(&engine, ());
2209
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2210
let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),)>(&mut store, "foo")?;
2211
assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),));
2212
2213
Ok(())
2214
}
2215
2216
#[test]
2217
fn option() -> Result<()> {
2218
let component = format!(
2219
r#"(component
2220
(core module $m
2221
(memory (export "memory") 1)
2222
(func (export "pass1") (param i32 i32) (result i32)
2223
(local $base i32)
2224
(local.set $base
2225
(call $realloc
2226
(i32.const 0)
2227
(i32.const 0)
2228
(i32.const 4)
2229
(i32.const 8)))
2230
2231
(i32.store offset=0
2232
(local.get $base)
2233
(local.get 0))
2234
(i32.store offset=4
2235
(local.get $base)
2236
(local.get 1))
2237
2238
(local.get $base)
2239
)
2240
(func (export "pass2") (param i32 i32 i32) (result i32)
2241
(local $base i32)
2242
(local.set $base
2243
(call $realloc
2244
(i32.const 0)
2245
(i32.const 0)
2246
(i32.const 4)
2247
(i32.const 12)))
2248
2249
(i32.store offset=0
2250
(local.get $base)
2251
(local.get 0))
2252
(i32.store offset=4
2253
(local.get $base)
2254
(local.get 1))
2255
(i32.store offset=8
2256
(local.get $base)
2257
(local.get 2))
2258
2259
(local.get $base)
2260
)
2261
2262
{REALLOC_AND_FREE}
2263
)
2264
(core instance $i (instantiate $m))
2265
2266
(func (export "option-u8-to-tuple") (param "a" (option u8)) (result (tuple u32 u32))
2267
(canon lift (core func $i "pass1") (memory $i "memory"))
2268
)
2269
(func (export "option-u32-to-tuple") (param "a" (option u32)) (result (tuple u32 u32))
2270
(canon lift (core func $i "pass1") (memory $i "memory"))
2271
)
2272
(func (export "option-string-to-tuple") (param "a" (option string)) (result (tuple u32 string))
2273
(canon lift
2274
(core func $i "pass2")
2275
(memory $i "memory")
2276
(realloc (func $i "realloc"))
2277
)
2278
)
2279
(func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8))
2280
(canon lift (core func $i "pass1") (memory $i "memory"))
2281
)
2282
(func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32))
2283
(canon lift
2284
(core func $i "pass1")
2285
(memory $i "memory")
2286
)
2287
)
2288
(func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string))
2289
(canon lift
2290
(core func $i "pass2")
2291
(memory $i "memory")
2292
(realloc (func $i "realloc"))
2293
)
2294
)
2295
)"#
2296
);
2297
2298
let engine = super::engine();
2299
let component = Component::new(&engine, component)?;
2300
let mut store = Store::new(&engine, ());
2301
let linker = Linker::new(&engine);
2302
let instance = linker.instantiate(&mut store, &component)?;
2303
2304
let option_u8_to_tuple = instance
2305
.get_typed_func::<(Option<u8>,), ((u32, u32),)>(&mut store, "option-u8-to-tuple")?;
2306
assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, ((0, 0),));
2307
option_u8_to_tuple.post_return(&mut store)?;
2308
assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));
2309
option_u8_to_tuple.post_return(&mut store)?;
2310
assert_eq!(
2311
option_u8_to_tuple.call(&mut store, (Some(100),))?,
2312
((1, 100),)
2313
);
2314
option_u8_to_tuple.post_return(&mut store)?;
2315
2316
let option_u32_to_tuple = instance
2317
.get_typed_func::<(Option<u32>,), ((u32, u32),)>(&mut store, "option-u32-to-tuple")?;
2318
assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, ((0, 0),));
2319
option_u32_to_tuple.post_return(&mut store)?;
2320
assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));
2321
option_u32_to_tuple.post_return(&mut store)?;
2322
assert_eq!(
2323
option_u32_to_tuple.call(&mut store, (Some(100),))?,
2324
((1, 100),)
2325
);
2326
option_u32_to_tuple.post_return(&mut store)?;
2327
2328
let option_string_to_tuple = instance.get_typed_func::<(Option<&str>,), ((u32, WasmStr),)>(
2329
&mut store,
2330
"option-string-to-tuple",
2331
)?;
2332
let ((a, b),) = option_string_to_tuple.call(&mut store, (None,))?;
2333
assert_eq!(a, 0);
2334
assert_eq!(b.to_str(&store)?, "");
2335
option_string_to_tuple.post_return(&mut store)?;
2336
let ((a, b),) = option_string_to_tuple.call(&mut store, (Some(""),))?;
2337
assert_eq!(a, 1);
2338
assert_eq!(b.to_str(&store)?, "");
2339
option_string_to_tuple.post_return(&mut store)?;
2340
let ((a, b),) = option_string_to_tuple.call(&mut store, (Some("hello"),))?;
2341
assert_eq!(a, 1);
2342
assert_eq!(b.to_str(&store)?, "hello");
2343
option_string_to_tuple.post_return(&mut store)?;
2344
2345
let instance = linker.instantiate(&mut store, &component)?;
2346
let to_option_u8 =
2347
instance.get_typed_func::<(u32, u32), (Option<u8>,)>(&mut store, "to-option-u8")?;
2348
assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,));
2349
to_option_u8.post_return(&mut store)?;
2350
assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),));
2351
to_option_u8.post_return(&mut store)?;
2352
assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),));
2353
to_option_u8.post_return(&mut store)?;
2354
assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err());
2355
2356
let instance = linker.instantiate(&mut store, &component)?;
2357
let to_option_u32 =
2358
instance.get_typed_func::<(u32, u32), (Option<u32>,)>(&mut store, "to-option-u32")?;
2359
assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,));
2360
to_option_u32.post_return(&mut store)?;
2361
assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),));
2362
to_option_u32.post_return(&mut store)?;
2363
assert_eq!(
2364
to_option_u32.call(&mut store, (1, 0x1234fead))?,
2365
(Some(0x1234fead),)
2366
);
2367
to_option_u32.post_return(&mut store)?;
2368
assert!(to_option_u32.call(&mut store, (2, 0)).is_err());
2369
2370
let instance = linker.instantiate(&mut store, &component)?;
2371
let to_option_string = instance
2372
.get_typed_func::<(u32, &str), (Option<WasmStr>,)>(&mut store, "to-option-string")?;
2373
let ret = to_option_string.call(&mut store, (0, ""))?.0;
2374
assert!(ret.is_none());
2375
to_option_string.post_return(&mut store)?;
2376
let ret = to_option_string.call(&mut store, (1, ""))?.0;
2377
assert_eq!(ret.unwrap().to_str(&store)?, "");
2378
to_option_string.post_return(&mut store)?;
2379
let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0;
2380
assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake");
2381
to_option_string.post_return(&mut store)?;
2382
assert!(to_option_string.call(&mut store, (2, "")).is_err());
2383
2384
Ok(())
2385
}
2386
2387
#[test]
2388
fn expected() -> Result<()> {
2389
let component = format!(
2390
r#"(component
2391
(core module $m
2392
(memory (export "memory") 1)
2393
(func (export "pass0") (param i32) (result i32)
2394
local.get 0
2395
)
2396
(func (export "pass1") (param i32 i32) (result i32)
2397
(local $base i32)
2398
(local.set $base
2399
(call $realloc
2400
(i32.const 0)
2401
(i32.const 0)
2402
(i32.const 4)
2403
(i32.const 8)))
2404
2405
(i32.store offset=0
2406
(local.get $base)
2407
(local.get 0))
2408
(i32.store offset=4
2409
(local.get $base)
2410
(local.get 1))
2411
2412
(local.get $base)
2413
)
2414
(func (export "pass2") (param i32 i32 i32) (result i32)
2415
(local $base i32)
2416
(local.set $base
2417
(call $realloc
2418
(i32.const 0)
2419
(i32.const 0)
2420
(i32.const 4)
2421
(i32.const 12)))
2422
2423
(i32.store offset=0
2424
(local.get $base)
2425
(local.get 0))
2426
(i32.store offset=4
2427
(local.get $base)
2428
(local.get 1))
2429
(i32.store offset=8
2430
(local.get $base)
2431
(local.get 2))
2432
2433
(local.get $base)
2434
)
2435
2436
{REALLOC_AND_FREE}
2437
)
2438
(core instance $i (instantiate $m))
2439
2440
(func (export "take-expected-unit") (param "a" (result)) (result u32)
2441
(canon lift (core func $i "pass0"))
2442
)
2443
(func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result (tuple u32 u32))
2444
(canon lift (core func $i "pass1") (memory $i "memory"))
2445
)
2446
(type $list (list u8))
2447
(func (export "take-expected-string") (param "a" (result string (error $list))) (result (tuple u32 string))
2448
(canon lift
2449
(core func $i "pass2")
2450
(memory $i "memory")
2451
(realloc (func $i "realloc"))
2452
)
2453
)
2454
(func (export "to-expected-unit") (param "a" u32) (result (result))
2455
(canon lift (core func $i "pass0"))
2456
)
2457
(func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32)))
2458
(canon lift
2459
(core func $i "pass1")
2460
(memory $i "memory")
2461
(realloc (func $i "realloc"))
2462
)
2463
)
2464
)"#
2465
);
2466
2467
let engine = super::engine();
2468
let component = Component::new(&engine, component)?;
2469
let mut store = Store::new(&engine, ());
2470
let linker = Linker::new(&engine);
2471
let instance = linker.instantiate(&mut store, &component)?;
2472
let take_expected_unit =
2473
instance.get_typed_func::<(Result<(), ()>,), (u32,)>(&mut store, "take-expected-unit")?;
2474
assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,));
2475
take_expected_unit.post_return(&mut store)?;
2476
assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,));
2477
take_expected_unit.post_return(&mut store)?;
2478
2479
let take_expected_u8_f32 = instance
2480
.get_typed_func::<(Result<u8, f32>,), ((u32, u32),)>(&mut store, "take-expected-u8-f32")?;
2481
assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, ((0, 1),));
2482
take_expected_u8_f32.post_return(&mut store)?;
2483
assert_eq!(
2484
take_expected_u8_f32.call(&mut store, (Err(2.0),))?,
2485
((1, 2.0f32.to_bits()),)
2486
);
2487
take_expected_u8_f32.post_return(&mut store)?;
2488
2489
let take_expected_string = instance
2490
.get_typed_func::<(Result<&str, &[u8]>,), ((u32, WasmStr),)>(
2491
&mut store,
2492
"take-expected-string",
2493
)?;
2494
let ((a, b),) = take_expected_string.call(&mut store, (Ok("hello"),))?;
2495
assert_eq!(a, 0);
2496
assert_eq!(b.to_str(&store)?, "hello");
2497
take_expected_string.post_return(&mut store)?;
2498
let ((a, b),) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?;
2499
assert_eq!(a, 1);
2500
assert_eq!(b.to_str(&store)?, "goodbye");
2501
take_expected_string.post_return(&mut store)?;
2502
2503
let instance = linker.instantiate(&mut store, &component)?;
2504
let to_expected_unit =
2505
instance.get_typed_func::<(u32,), (Result<(), ()>,)>(&mut store, "to-expected-unit")?;
2506
assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),));
2507
to_expected_unit.post_return(&mut store)?;
2508
assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),));
2509
to_expected_unit.post_return(&mut store)?;
2510
let err = to_expected_unit.call(&mut store, (2,)).unwrap_err();
2511
assert!(err.to_string().contains("invalid expected"), "{}", err);
2512
2513
let instance = linker.instantiate(&mut store, &component)?;
2514
let to_expected_s16_f32 = instance
2515
.get_typed_func::<(u32, u32), (Result<i16, f32>,)>(&mut store, "to-expected-s16-f32")?;
2516
assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),));
2517
to_expected_s16_f32.post_return(&mut store)?;
2518
assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),));
2519
to_expected_s16_f32.post_return(&mut store)?;
2520
assert_eq!(
2521
to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?,
2522
(Err(1.0),)
2523
);
2524
to_expected_s16_f32.post_return(&mut store)?;
2525
let ret = to_expected_s16_f32
2526
.call(&mut store, (1, CANON_32BIT_NAN | 1))?
2527
.0;
2528
assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN | 1);
2529
to_expected_s16_f32.post_return(&mut store)?;
2530
assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err());
2531
2532
Ok(())
2533
}
2534
2535
#[test]
2536
fn fancy_list() -> Result<()> {
2537
let component = format!(
2538
r#"(component
2539
(core module $m
2540
(memory (export "memory") 1)
2541
(func (export "take") (param i32 i32) (result i32)
2542
(local $base i32)
2543
(local.set $base
2544
(call $realloc
2545
(i32.const 0)
2546
(i32.const 0)
2547
(i32.const 4)
2548
(i32.const 16)))
2549
2550
(i32.store offset=0
2551
(local.get $base)
2552
(local.get 0))
2553
(i32.store offset=4
2554
(local.get $base)
2555
(local.get 1))
2556
(i32.store offset=8
2557
(local.get $base)
2558
(i32.const 0))
2559
(i32.store offset=12
2560
(local.get $base)
2561
(i32.mul
2562
(memory.size)
2563
(i32.const 65536)))
2564
2565
(local.get $base)
2566
)
2567
2568
{REALLOC_AND_FREE}
2569
)
2570
(core instance $i (instantiate $m))
2571
2572
(type $a (option u8))
2573
(type $b (result (error string)))
2574
(type $input (list (tuple $a $b)))
2575
(func (export "take")
2576
(param "a" $input)
2577
(result (tuple u32 u32 (list u8)))
2578
(canon lift
2579
(core func $i "take")
2580
(memory $i "memory")
2581
(realloc (func $i "realloc"))
2582
)
2583
)
2584
)"#
2585
);
2586
2587
let engine = super::engine();
2588
let component = Component::new(&engine, component)?;
2589
let mut store = Store::new(&engine, ());
2590
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2591
2592
let func = instance
2593
.get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), ((u32, u32, WasmList<u8>),)>(
2594
&mut store, "take",
2595
)?;
2596
2597
let input = [
2598
(None, Ok(())),
2599
(Some(2), Err("hello there")),
2600
(Some(200), Err("general kenobi")),
2601
];
2602
let ((ptr, len, list),) = func.call(&mut store, (&input,))?;
2603
let memory = list.as_le_slice(&store);
2604
let ptr = usize::try_from(ptr).unwrap();
2605
let len = usize::try_from(len).unwrap();
2606
let mut array = &memory[ptr..][..len * 16];
2607
2608
for (a, b) in input.iter() {
2609
match a {
2610
Some(val) => {
2611
assert_eq!(*array.take_n::<2>(), [1, *val]);
2612
}
2613
None => {
2614
assert_eq!(*array.take_n::<1>(), [0]);
2615
array.skip::<1>();
2616
}
2617
}
2618
array.skip::<2>();
2619
match b {
2620
Ok(()) => {
2621
assert_eq!(*array.take_n::<1>(), [0]);
2622
array.skip::<11>();
2623
}
2624
Err(s) => {
2625
assert_eq!(*array.take_n::<1>(), [1]);
2626
array.skip::<3>();
2627
assert_eq!(array.ptr_len(memory, 1), s.as_bytes());
2628
}
2629
}
2630
}
2631
assert!(array.is_empty());
2632
2633
Ok(())
2634
}
2635
2636
trait SliceExt<'a> {
2637
fn take_n<const N: usize>(&mut self) -> &'a [u8; N];
2638
2639
fn skip<const N: usize>(&mut self) {
2640
self.take_n::<N>();
2641
}
2642
2643
fn ptr_len<'b>(&mut self, all_memory: &'b [u8], size: usize) -> &'b [u8] {
2644
let ptr = u32::from_le_bytes(*self.take_n::<4>());
2645
let len = u32::from_le_bytes(*self.take_n::<4>());
2646
let ptr = usize::try_from(ptr).unwrap();
2647
let len = usize::try_from(len).unwrap();
2648
&all_memory[ptr..][..len * size]
2649
}
2650
}
2651
2652
impl<'a> SliceExt<'a> for &'a [u8] {
2653
fn take_n<const N: usize>(&mut self) -> &'a [u8; N] {
2654
let (a, b) = self.split_at(N);
2655
*self = b;
2656
a.try_into().unwrap()
2657
}
2658
}
2659
2660
#[test]
2661
fn invalid_alignment() -> Result<()> {
2662
let component = format!(
2663
r#"(component
2664
(core module $m
2665
(memory (export "memory") 1)
2666
(func (export "realloc") (param i32 i32 i32 i32) (result i32)
2667
i32.const 1)
2668
2669
(func (export "take-i32") (param i32))
2670
(func (export "ret-1") (result i32) i32.const 1)
2671
(func (export "ret-unaligned-list") (result i32)
2672
(i32.store offset=0 (i32.const 8) (i32.const 1))
2673
(i32.store offset=4 (i32.const 8) (i32.const 1))
2674
i32.const 8)
2675
)
2676
(core instance $i (instantiate $m))
2677
2678
(func (export "many-params")
2679
(param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string)
2680
(param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string)
2681
(param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string)
2682
(canon lift
2683
(core func $i "take-i32")
2684
(memory $i "memory")
2685
(realloc (func $i "realloc"))
2686
)
2687
)
2688
(func (export "string-ret") (result string)
2689
(canon lift
2690
(core func $i "ret-1")
2691
(memory $i "memory")
2692
(realloc (func $i "realloc"))
2693
)
2694
)
2695
(func (export "list-u32-ret") (result (list u32))
2696
(canon lift
2697
(core func $i "ret-unaligned-list")
2698
(memory $i "memory")
2699
(realloc (func $i "realloc"))
2700
)
2701
)
2702
)"#
2703
);
2704
2705
let engine = super::engine();
2706
let component = Component::new(&engine, component)?;
2707
let mut store = Store::new(&engine, ());
2708
let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component);
2709
2710
let err = instance(&mut store)?
2711
.get_typed_func::<(
2712
&str,
2713
&str,
2714
&str,
2715
&str,
2716
&str,
2717
&str,
2718
&str,
2719
&str,
2720
&str,
2721
&str,
2722
&str,
2723
&str,
2724
), ()>(&mut store, "many-params")?
2725
.call(&mut store, ("", "", "", "", "", "", "", "", "", "", "", ""))
2726
.unwrap_err();
2727
assert!(
2728
err.to_string()
2729
.contains("realloc return: result not aligned"),
2730
"{}",
2731
err
2732
);
2733
2734
let err = instance(&mut store)?
2735
.get_typed_func::<(), (WasmStr,)>(&mut store, "string-ret")?
2736
.call(&mut store, ())
2737
.err()
2738
.unwrap();
2739
assert!(
2740
err.to_string().contains("return pointer not aligned"),
2741
"{}",
2742
err
2743
);
2744
2745
let err = instance(&mut store)?
2746
.get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32-ret")?
2747
.call(&mut store, ())
2748
.err()
2749
.unwrap();
2750
assert!(
2751
err.to_string().contains("list pointer is not aligned"),
2752
"{}",
2753
err
2754
);
2755
2756
Ok(())
2757
}
2758
2759
#[test]
2760
fn drop_component_still_works() -> Result<()> {
2761
let component = r#"
2762
(component
2763
(import "f" (func $f))
2764
2765
(core func $f_lower
2766
(canon lower (func $f))
2767
)
2768
(core module $m
2769
(import "" "" (func $f))
2770
2771
(func $f2
2772
call $f
2773
call $f
2774
)
2775
2776
(export "f" (func $f2))
2777
)
2778
(core instance $i (instantiate $m
2779
(with "" (instance
2780
(export "" (func $f_lower))
2781
))
2782
))
2783
(func (export "g")
2784
(canon lift
2785
(core func $i "f")
2786
)
2787
)
2788
)
2789
"#;
2790
2791
let (mut store, instance) = {
2792
let engine = super::engine();
2793
let component = Component::new(&engine, component)?;
2794
let mut store = Store::new(&engine, 0);
2795
let mut linker = Linker::new(&engine);
2796
linker.root().func_wrap(
2797
"f",
2798
|mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> {
2799
*store.data_mut() += 1;
2800
Ok(())
2801
},
2802
)?;
2803
let instance = linker.instantiate(&mut store, &component)?;
2804
(store, instance)
2805
};
2806
2807
let f = instance.get_typed_func::<(), ()>(&mut store, "g")?;
2808
assert_eq!(*store.data(), 0);
2809
f.call(&mut store, ())?;
2810
assert_eq!(*store.data(), 2);
2811
2812
Ok(())
2813
}
2814
2815
#[test]
2816
fn raw_slice_of_various_types() -> Result<()> {
2817
let component = r#"
2818
(component
2819
(core module $m
2820
(memory (export "memory") 1)
2821
2822
(func (export "list8") (result i32)
2823
(call $setup_list (i32.const 16))
2824
)
2825
(func (export "list16") (result i32)
2826
(call $setup_list (i32.const 8))
2827
)
2828
(func (export "list32") (result i32)
2829
(call $setup_list (i32.const 4))
2830
)
2831
(func (export "list64") (result i32)
2832
(call $setup_list (i32.const 2))
2833
)
2834
2835
(func $setup_list (param i32) (result i32)
2836
(i32.store offset=0 (i32.const 100) (i32.const 8))
2837
(i32.store offset=4 (i32.const 100) (local.get 0))
2838
i32.const 100
2839
)
2840
2841
(data (i32.const 8) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f")
2842
)
2843
(core instance $i (instantiate $m))
2844
(func (export "list-u8") (result (list u8))
2845
(canon lift (core func $i "list8") (memory $i "memory"))
2846
)
2847
(func (export "list-i8") (result (list s8))
2848
(canon lift (core func $i "list8") (memory $i "memory"))
2849
)
2850
(func (export "list-u16") (result (list u16))
2851
(canon lift (core func $i "list16") (memory $i "memory"))
2852
)
2853
(func (export "list-i16") (result (list s16))
2854
(canon lift (core func $i "list16") (memory $i "memory"))
2855
)
2856
(func (export "list-u32") (result (list u32))
2857
(canon lift (core func $i "list32") (memory $i "memory"))
2858
)
2859
(func (export "list-i32") (result (list s32))
2860
(canon lift (core func $i "list32") (memory $i "memory"))
2861
)
2862
(func (export "list-u64") (result (list u64))
2863
(canon lift (core func $i "list64") (memory $i "memory"))
2864
)
2865
(func (export "list-i64") (result (list s64))
2866
(canon lift (core func $i "list64") (memory $i "memory"))
2867
)
2868
)
2869
"#;
2870
2871
let (mut store, instance) = {
2872
let engine = super::engine();
2873
let component = Component::new(&engine, component)?;
2874
let mut store = Store::new(&engine, ());
2875
let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2876
(store, instance)
2877
};
2878
2879
let list = instance
2880
.get_typed_func::<(), (WasmList<u8>,)>(&mut store, "list-u8")?
2881
.call_and_post_return(&mut store, ())?
2882
.0;
2883
assert_eq!(
2884
list.as_le_slice(&store),
2885
[
2886
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
2887
0x0e, 0x0f,
2888
]
2889
);
2890
let list = instance
2891
.get_typed_func::<(), (WasmList<i8>,)>(&mut store, "list-i8")?
2892
.call_and_post_return(&mut store, ())?
2893
.0;
2894
assert_eq!(
2895
list.as_le_slice(&store),
2896
[
2897
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
2898
0x0e, 0x0f,
2899
]
2900
);
2901
2902
let list = instance
2903
.get_typed_func::<(), (WasmList<u16>,)>(&mut store, "list-u16")?
2904
.call_and_post_return(&mut store, ())?
2905
.0;
2906
assert_eq!(
2907
list.as_le_slice(&store),
2908
[
2909
u16::to_le(0x01_00),
2910
u16::to_le(0x03_02),
2911
u16::to_le(0x05_04),
2912
u16::to_le(0x07_06),
2913
u16::to_le(0x09_08),
2914
u16::to_le(0x0b_0a),
2915
u16::to_le(0x0d_0c),
2916
u16::to_le(0x0f_0e),
2917
]
2918
);
2919
let list = instance
2920
.get_typed_func::<(), (WasmList<i16>,)>(&mut store, "list-i16")?
2921
.call_and_post_return(&mut store, ())?
2922
.0;
2923
assert_eq!(
2924
list.as_le_slice(&store),
2925
[
2926
i16::to_le(0x01_00),
2927
i16::to_le(0x03_02),
2928
i16::to_le(0x05_04),
2929
i16::to_le(0x07_06),
2930
i16::to_le(0x09_08),
2931
i16::to_le(0x0b_0a),
2932
i16::to_le(0x0d_0c),
2933
i16::to_le(0x0f_0e),
2934
]
2935
);
2936
let list = instance
2937
.get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32")?
2938
.call_and_post_return(&mut store, ())?
2939
.0;
2940
assert_eq!(
2941
list.as_le_slice(&store),
2942
[
2943
u32::to_le(0x03_02_01_00),
2944
u32::to_le(0x07_06_05_04),
2945
u32::to_le(0x0b_0a_09_08),
2946
u32::to_le(0x0f_0e_0d_0c),
2947
]
2948
);
2949
let list = instance
2950
.get_typed_func::<(), (WasmList<i32>,)>(&mut store, "list-i32")?
2951
.call_and_post_return(&mut store, ())?
2952
.0;
2953
assert_eq!(
2954
list.as_le_slice(&store),
2955
[
2956
i32::to_le(0x03_02_01_00),
2957
i32::to_le(0x07_06_05_04),
2958
i32::to_le(0x0b_0a_09_08),
2959
i32::to_le(0x0f_0e_0d_0c),
2960
]
2961
);
2962
let list = instance
2963
.get_typed_func::<(), (WasmList<u64>,)>(&mut store, "list-u64")?
2964
.call_and_post_return(&mut store, ())?
2965
.0;
2966
assert_eq!(
2967
list.as_le_slice(&store),
2968
[
2969
u64::to_le(0x07_06_05_04_03_02_01_00),
2970
u64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
2971
]
2972
);
2973
let list = instance
2974
.get_typed_func::<(), (WasmList<i64>,)>(&mut store, "list-i64")?
2975
.call_and_post_return(&mut store, ())?
2976
.0;
2977
assert_eq!(
2978
list.as_le_slice(&store),
2979
[
2980
i64::to_le(0x07_06_05_04_03_02_01_00),
2981
i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
2982
]
2983
);
2984
2985
Ok(())
2986
}
2987
2988
#[test]
2989
fn lower_then_lift() -> Result<()> {
2990
// First test simple integers when the import/export ABI happen to line up
2991
let component = r#"
2992
(component $c
2993
(import "f" (func $f (result u32)))
2994
2995
(core func $f_lower
2996
(canon lower (func $f))
2997
)
2998
(func $f2 (result s32)
2999
(canon lift (core func $f_lower))
3000
)
3001
(export "f2" (func $f2))
3002
)
3003
"#;
3004
3005
let engine = super::engine();
3006
let component = Component::new(&engine, component)?;
3007
let mut store = Store::new(&engine, ());
3008
let mut linker = Linker::new(&engine);
3009
linker.root().func_wrap("f", |_, _: ()| Ok((2u32,)))?;
3010
let instance = linker.instantiate(&mut store, &component)?;
3011
3012
let f = instance.get_typed_func::<(), (i32,)>(&mut store, "f2")?;
3013
assert_eq!(f.call(&mut store, ())?, (2,));
3014
3015
// First test strings when the import/export ABI happen to line up
3016
let component = format!(
3017
r#"
3018
(component $c
3019
(import "s" (func $f (param "a" string)))
3020
3021
(core module $libc
3022
(memory (export "memory") 1)
3023
{REALLOC_AND_FREE}
3024
)
3025
(core instance $libc (instantiate $libc))
3026
3027
(core func $f_lower
3028
(canon lower (func $f) (memory $libc "memory"))
3029
)
3030
(func $f2 (param "a" string)
3031
(canon lift (core func $f_lower)
3032
(memory $libc "memory")
3033
(realloc (func $libc "realloc"))
3034
)
3035
)
3036
(export "f" (func $f2))
3037
)
3038
"#
3039
);
3040
3041
let component = Component::new(&engine, component)?;
3042
let mut store = Store::new(&engine, ());
3043
linker
3044
.root()
3045
.func_wrap("s", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| {
3046
assert_eq!(x.to_str(&store)?, "hello");
3047
Ok(())
3048
})?;
3049
let instance = linker.instantiate(&mut store, &component)?;
3050
3051
let f = instance.get_typed_func::<(&str,), ()>(&mut store, "f")?;
3052
f.call(&mut store, ("hello",))?;
3053
3054
// Next test "type punning" where return values are reinterpreted just
3055
// because the return ABI happens to line up.
3056
let component = format!(
3057
r#"
3058
(component $c
3059
(import "s2" (func $f (param "a" string) (result u32)))
3060
3061
(core module $libc
3062
(memory (export "memory") 1)
3063
{REALLOC_AND_FREE}
3064
)
3065
(core instance $libc (instantiate $libc))
3066
3067
(core func $f_lower
3068
(canon lower (func $f) (memory $libc "memory"))
3069
)
3070
(func $f2 (param "a" string) (result string)
3071
(canon lift (core func $f_lower)
3072
(memory $libc "memory")
3073
(realloc (func $libc "realloc"))
3074
)
3075
)
3076
(export "f" (func $f2))
3077
)
3078
"#
3079
);
3080
3081
let component = Component::new(&engine, component)?;
3082
let mut store = Store::new(&engine, ());
3083
linker
3084
.root()
3085
.func_wrap("s2", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| {
3086
assert_eq!(x.to_str(&store)?, "hello");
3087
Ok((u32::MAX,))
3088
})?;
3089
let instance = linker.instantiate(&mut store, &component)?;
3090
3091
let f = instance.get_typed_func::<(&str,), (WasmStr,)>(&mut store, "f")?;
3092
let err = f.call(&mut store, ("hello",)).err().unwrap();
3093
assert!(
3094
err.to_string().contains("return pointer not aligned"),
3095
"{}",
3096
err
3097
);
3098
3099
Ok(())
3100
}
3101
3102
#[test]
3103
fn errors_that_poison_instance() -> Result<()> {
3104
let component = format!(
3105
r#"
3106
(component $c
3107
(core module $m1
3108
(func (export "f1") unreachable)
3109
(func (export "f2"))
3110
)
3111
(core instance $m1 (instantiate $m1))
3112
(func (export "f1") (canon lift (core func $m1 "f1")))
3113
(func (export "f2") (canon lift (core func $m1 "f2")))
3114
3115
(core module $m2
3116
(func (export "f") (param i32 i32))
3117
(func (export "r") (param i32 i32 i32 i32) (result i32) unreachable)
3118
(memory (export "m") 1)
3119
)
3120
(core instance $m2 (instantiate $m2))
3121
(func (export "f3") (param "a" string)
3122
(canon lift (core func $m2 "f") (realloc (func $m2 "r")) (memory $m2 "m"))
3123
)
3124
3125
(core module $m3
3126
(func (export "f") (result i32) i32.const 1)
3127
(memory (export "m") 1)
3128
)
3129
(core instance $m3 (instantiate $m3))
3130
(func (export "f4") (result string)
3131
(canon lift (core func $m3 "f") (memory $m3 "m"))
3132
)
3133
)
3134
"#
3135
);
3136
3137
let engine = super::engine();
3138
let component = Component::new(&engine, component)?;
3139
let mut store = Store::new(&engine, ());
3140
let linker = Linker::new(&engine);
3141
let instance = linker.instantiate(&mut store, &component)?;
3142
let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?;
3143
let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?;
3144
assert_unreachable(f1.call(&mut store, ()));
3145
assert_poisoned(f1.call(&mut store, ()));
3146
assert_poisoned(f2.call(&mut store, ()));
3147
3148
let instance = linker.instantiate(&mut store, &component)?;
3149
let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?;
3150
assert_unreachable(f3.call(&mut store, ("x",)));
3151
assert_poisoned(f3.call(&mut store, ("x",)));
3152
3153
let instance = linker.instantiate(&mut store, &component)?;
3154
let f4 = instance.get_typed_func::<(), (WasmStr,)>(&mut store, "f4")?;
3155
assert!(f4.call(&mut store, ()).is_err());
3156
assert_poisoned(f4.call(&mut store, ()));
3157
3158
return Ok(());
3159
3160
#[track_caller]
3161
fn assert_unreachable<T>(err: Result<T>) {
3162
let err = match err {
3163
Ok(_) => panic!("expected an error"),
3164
Err(e) => e,
3165
};
3166
assert_eq!(
3167
err.downcast::<Trap>().unwrap(),
3168
Trap::UnreachableCodeReached
3169
);
3170
}
3171
3172
#[track_caller]
3173
fn assert_poisoned<T>(err: Result<T>) {
3174
let err = match err {
3175
Ok(_) => panic!("expected an error"),
3176
Err(e) => e,
3177
};
3178
assert_eq!(
3179
err.downcast_ref::<Trap>(),
3180
Some(&Trap::CannotEnterComponent),
3181
"{err}",
3182
);
3183
}
3184
}
3185
3186
#[test]
3187
fn run_export_with_internal_adapter() -> Result<()> {
3188
let component = r#"
3189
(component
3190
(type $t (func (param "a" u32) (result u32)))
3191
(component $a
3192
(core module $m
3193
(func (export "add-five") (param i32) (result i32)
3194
local.get 0
3195
i32.const 5
3196
i32.add)
3197
)
3198
(core instance $m (instantiate $m))
3199
(func (export "add-five") (type $t) (canon lift (core func $m "add-five")))
3200
)
3201
(component $b
3202
(import "interface-v1" (instance $i
3203
(export "add-five" (func (type $t)))))
3204
(core module $m
3205
(func $add-five (import "interface-0.1.0" "add-five") (param i32) (result i32))
3206
(func) ;; causes index out of bounds
3207
(func (export "run") (result i32) i32.const 0 call $add-five)
3208
)
3209
(core func $add-five (canon lower (func $i "add-five")))
3210
(core instance $i (instantiate 0
3211
(with "interface-0.1.0" (instance
3212
(export "add-five" (func $add-five))
3213
))
3214
))
3215
(func (result u32) (canon lift (core func $i "run")))
3216
(export "run" (func 1))
3217
)
3218
(instance $a (instantiate $a))
3219
(instance $b (instantiate $b (with "interface-v1" (instance $a))))
3220
(export "run" (func $b "run"))
3221
)
3222
"#;
3223
let engine = super::engine();
3224
let component = Component::new(&engine, component)?;
3225
let mut store = Store::new(&engine, ());
3226
let linker = Linker::new(&engine);
3227
let instance = linker.instantiate(&mut store, &component)?;
3228
let run = instance.get_typed_func::<(), (u32,)>(&mut store, "run")?;
3229
assert_eq!(run.call(&mut store, ())?, (5,));
3230
Ok(())
3231
}
3232
3233