Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/component_model/bindgen/results.rs
1692 views
1
use super::{super::REALLOC_AND_FREE, engine};
2
use anyhow::{Error, anyhow};
3
use wasmtime::{
4
Store,
5
component::{Component, Linker},
6
};
7
8
mod empty_error {
9
use super::*;
10
use wasmtime::component::HasSelf;
11
12
wasmtime::component::bindgen!({
13
inline: "
14
package inline:inline;
15
world result-playground {
16
import imports: interface {
17
empty-error: func(a: f64) -> result<f64>;
18
}
19
20
export empty-error: func(a: f64) -> result<f64>;
21
}",
22
imports: {default: trappable},
23
});
24
25
#[test]
26
fn run() -> Result<(), Error> {
27
let engine = engine();
28
let component = Component::new(
29
&engine,
30
r#"
31
(component
32
(import "imports" (instance $i
33
(export "empty-error" (func (param "a" f64) (result (result f64))))
34
))
35
(core module $libc
36
(memory (export "memory") 1)
37
)
38
(core instance $libc (instantiate $libc))
39
(core module $m
40
(import "" "core_empty_error" (func $f (param f64 i32)))
41
(import "libc" "memory" (memory 0))
42
(func (export "core_empty_error_export") (param f64) (result i32)
43
(call $f (local.get 0) (i32.const 8))
44
(i32.const 8)
45
)
46
)
47
(core func $core_empty_error
48
(canon lower (func $i "empty-error") (memory $libc "memory"))
49
)
50
(core instance $i (instantiate $m
51
(with "" (instance (export "core_empty_error" (func $core_empty_error))))
52
(with "libc" (instance $libc))
53
))
54
(func $f_empty_error
55
(export "empty-error")
56
(param "a" f64)
57
(result (result f64))
58
(canon lift (core func $i "core_empty_error_export") (memory $libc "memory"))
59
)
60
)
61
"#,
62
)?;
63
64
#[derive(Default)]
65
struct MyImports {}
66
67
impl imports::Host for MyImports {
68
fn empty_error(&mut self, a: f64) -> Result<Result<f64, ()>, Error> {
69
if a == 0.0 {
70
Ok(Ok(a))
71
} else if a == 1.0 {
72
Ok(Err(()))
73
} else {
74
Err(anyhow!("empty_error: trap"))
75
}
76
}
77
}
78
79
let mut linker = Linker::new(&engine);
80
imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
81
82
let mut store = Store::new(&engine, MyImports::default());
83
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
84
85
assert_eq!(
86
results
87
.call_empty_error(&mut store, 0.0)
88
.expect("no trap")
89
.expect("no error returned"),
90
0.0
91
);
92
93
results
94
.call_empty_error(&mut store, 1.0)
95
.expect("no trap")
96
.err()
97
.expect("() error returned");
98
99
let e = results
100
.call_empty_error(&mut store, 2.0)
101
.err()
102
.expect("trap");
103
assert_eq!(
104
format!("{}", e.source().expect("trap message is stored in source")),
105
"empty_error: trap"
106
);
107
108
Ok(())
109
}
110
}
111
112
mod string_error {
113
use super::*;
114
use wasmtime::component::HasSelf;
115
116
wasmtime::component::bindgen!({
117
inline: "
118
package inline:inline;
119
world result-playground {
120
import imports: interface {
121
string-error: func(a: f64) -> result<f64, string>;
122
}
123
124
export string-error: func(a: f64) -> result<f64, string>;
125
}",
126
imports: { default: trappable },
127
});
128
129
#[test]
130
fn run() -> Result<(), Error> {
131
let engine = engine();
132
let component = Component::new(
133
&engine,
134
format!(
135
r#"
136
(component
137
(import "imports" (instance $i
138
(export "string-error" (func (param "a" f64) (result (result f64 (error string)))))
139
))
140
(core module $libc
141
(memory (export "memory") 1)
142
{REALLOC_AND_FREE}
143
)
144
(core instance $libc (instantiate $libc))
145
(core module $m
146
(import "" "core_string_error" (func $f (param f64 i32)))
147
(import "libc" "memory" (memory 0))
148
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
149
(func (export "core_string_error_export") (param f64) (result i32)
150
(local $retptr i32)
151
(local.set $retptr
152
(call $realloc
153
(i32.const 0)
154
(i32.const 0)
155
(i32.const 4)
156
(i32.const 16)))
157
(call $f (local.get 0) (local.get $retptr))
158
(local.get $retptr)
159
)
160
)
161
(core func $core_string_error
162
(canon lower (func $i "string-error") (memory $libc "memory") (realloc (func $libc "realloc")))
163
)
164
(core instance $i (instantiate $m
165
(with "" (instance (export "core_string_error" (func $core_string_error))))
166
(with "libc" (instance $libc))
167
))
168
(func $f_string_error
169
(export "string-error")
170
(param "a" f64)
171
(result (result f64 (error string)))
172
(canon lift (core func $i "core_string_error_export") (memory $libc "memory"))
173
)
174
)
175
"#
176
),
177
)?;
178
179
#[derive(Default)]
180
struct MyImports {}
181
182
impl imports::Host for MyImports {
183
fn string_error(&mut self, a: f64) -> Result<Result<f64, String>, Error> {
184
if a == 0.0 {
185
Ok(Ok(a))
186
} else if a == 1.0 {
187
Ok(Err("string_error: error".to_owned()))
188
} else {
189
Err(anyhow!("string_error: trap"))
190
}
191
}
192
}
193
194
let mut linker = Linker::new(&engine);
195
imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
196
197
let mut store = Store::new(&engine, MyImports::default());
198
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
199
200
assert_eq!(
201
results
202
.call_string_error(&mut store, 0.0)
203
.expect("no trap")
204
.expect("no error returned"),
205
0.0
206
);
207
208
let e = results
209
.call_string_error(&mut store, 1.0)
210
.expect("no trap")
211
.err()
212
.expect("error returned");
213
assert_eq!(e, "string_error: error");
214
215
let e = results
216
.call_string_error(&mut store, 2.0)
217
.err()
218
.expect("trap");
219
assert_eq!(
220
format!("{}", e.source().expect("trap message is stored in source")),
221
"string_error: trap"
222
);
223
224
Ok(())
225
}
226
}
227
228
mod enum_error {
229
use super::*;
230
use exports::foo;
231
use inline::inline::imports;
232
use wasmtime::component::HasSelf;
233
234
wasmtime::component::bindgen!({
235
inline: "
236
package inline:inline;
237
interface imports {
238
enum e1 { a, b, c }
239
enum-error: func(a: f64) -> result<f64, e1>;
240
}
241
world result-playground {
242
import imports;
243
export foo: interface {
244
enum e1 { a, b, c }
245
enum-error: func(a: f64) -> result<f64, e1>;
246
}
247
}",
248
trappable_error_type: { "inline:inline/imports/e1" => TrappableE1 },
249
imports: { default: trappable },
250
});
251
252
// You can create concrete trap types which make it all the way out to the
253
// host caller, via downcast_ref below.
254
#[derive(Debug)]
255
pub struct MyTrap;
256
257
impl std::fmt::Display for MyTrap {
258
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259
write!(f, "{self:?}")
260
}
261
}
262
impl std::error::Error for MyTrap {}
263
264
pub enum TrappableE1 {
265
Normal(imports::E1),
266
MyTrap(MyTrap),
267
}
268
269
// It is possible to define From impls that target these generated trappable
270
// types. This allows you to integrate libraries with other error types, or
271
// use your own more descriptive error types, and use ? to convert them at
272
// their throw site.
273
impl From<MyTrap> for TrappableE1 {
274
fn from(t: MyTrap) -> TrappableE1 {
275
TrappableE1::MyTrap(t)
276
}
277
}
278
279
impl From<imports::E1> for TrappableE1 {
280
fn from(t: imports::E1) -> TrappableE1 {
281
TrappableE1::Normal(t)
282
}
283
}
284
285
#[test]
286
fn run() -> Result<(), Error> {
287
let engine = engine();
288
let component = Component::new(
289
&engine,
290
format!(
291
r#"
292
(component
293
(type $err' (enum "a" "b" "c"))
294
(import (interface "inline:inline/imports") (instance $i
295
(export "err" (type $err (eq $err')))
296
(export "enum-error" (func (param "a" f64) (result (result f64 (error $err)))))
297
))
298
(core module $libc
299
(memory (export "memory") 1)
300
{REALLOC_AND_FREE}
301
)
302
(core instance $libc (instantiate $libc))
303
(core module $m
304
(import "" "core_enum_error" (func $f (param f64 i32)))
305
(import "libc" "memory" (memory 0))
306
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
307
(func (export "core_enum_error_export") (param f64) (result i32)
308
(local $retptr i32)
309
(local.set $retptr
310
(call $realloc
311
(i32.const 0)
312
(i32.const 0)
313
(i32.const 4)
314
(i32.const 16)))
315
(call $f (local.get 0) (local.get $retptr))
316
(local.get $retptr)
317
)
318
)
319
(core func $core_enum_error
320
(canon lower (func $i "enum-error") (memory $libc "memory") (realloc (func $libc "realloc")))
321
)
322
(core instance $i (instantiate $m
323
(with "" (instance (export "core_enum_error" (func $core_enum_error))))
324
(with "libc" (instance $libc))
325
))
326
(func $f_enum_error
327
(param "a" f64)
328
(result (result f64 (error $err')))
329
(canon lift (core func $i "core_enum_error_export") (memory $libc "memory"))
330
)
331
332
(component $nested
333
(import "f-err" (type $err (eq $err')))
334
(import "f" (func $f (param "a" f64) (result (result f64 (error $err)))))
335
(export $err2 "err" (type $err'))
336
(export "enum-error" (func $f) (func (param "a" f64) (result (result f64 (error $err2)))))
337
)
338
339
(instance $n (instantiate $nested
340
(with "f-err" (type $err'))
341
(with "f" (func $f_enum_error))
342
))
343
(export "foo" (instance $n))
344
)
345
"#
346
),
347
)?;
348
349
#[derive(Default)]
350
struct MyImports {}
351
352
impl imports::Host for MyImports {
353
fn convert_e1(&mut self, err: TrappableE1) -> anyhow::Result<imports::E1> {
354
match err {
355
TrappableE1::Normal(e) => Ok(e),
356
TrappableE1::MyTrap(e) => Err(e.into()),
357
}
358
}
359
360
fn enum_error(&mut self, a: f64) -> Result<f64, TrappableE1> {
361
if a == 0.0 {
362
Ok(a)
363
} else if a == 1.0 {
364
Err(imports::E1::A)?
365
} else {
366
Err(MyTrap)?
367
}
368
}
369
}
370
371
let mut linker = Linker::new(&engine);
372
imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
373
374
let mut store = Store::new(&engine, MyImports::default());
375
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
376
377
assert_eq!(
378
results
379
.foo()
380
.call_enum_error(&mut store, 0.0)
381
.expect("no trap")
382
.expect("no error returned"),
383
0.0
384
);
385
386
let e = results
387
.foo()
388
.call_enum_error(&mut store, 1.0)
389
.expect("no trap")
390
.err()
391
.expect("error returned");
392
assert_eq!(e, foo::E1::A);
393
394
let e = results
395
.foo()
396
.call_enum_error(&mut store, 2.0)
397
.err()
398
.expect("trap");
399
assert_eq!(
400
format!("{}", e.source().expect("trap message is stored in source")),
401
"MyTrap"
402
);
403
e.downcast_ref::<MyTrap>()
404
.expect("downcast trap to concrete MyTrap type");
405
406
Ok(())
407
}
408
}
409
410
mod record_error {
411
use super::*;
412
use exports::foo;
413
use inline::inline::imports;
414
use wasmtime::component::HasSelf;
415
416
wasmtime::component::bindgen!({
417
inline: "
418
package inline:inline;
419
interface imports {
420
record e2 { line: u32, col: u32 }
421
record-error: func(a: f64) -> result<f64, e2>;
422
}
423
world result-playground {
424
import imports;
425
export foo: interface {
426
record e2 { line: u32, col: u32 }
427
record-error: func(a: f64) -> result<f64, e2>;
428
}
429
}",
430
trappable_error_type: { "inline:inline/imports/e2" => TrappableE2 },
431
imports: { default: trappable },
432
});
433
434
pub enum TrappableE2 {
435
Normal(imports::E2),
436
Trap(anyhow::Error),
437
}
438
439
impl From<imports::E2> for TrappableE2 {
440
fn from(t: imports::E2) -> TrappableE2 {
441
TrappableE2::Normal(t)
442
}
443
}
444
445
#[test]
446
fn run() -> Result<(), Error> {
447
let engine = engine();
448
let component = Component::new(
449
&engine,
450
format!(
451
r#"
452
(component
453
(type $e2' (record
454
(field "line" u32)
455
(field "col" u32)
456
))
457
(import (interface "inline:inline/imports") (instance $i
458
(export "e2" (type $e2 (eq $e2')))
459
(type $result (result f64 (error $e2)))
460
(export "record-error" (func (param "a" f64) (result $result)))
461
))
462
(core module $libc
463
(memory (export "memory") 1)
464
{REALLOC_AND_FREE}
465
)
466
(core instance $libc (instantiate $libc))
467
(core module $m
468
(import "" "core_record_error" (func $f (param f64 i32)))
469
(import "libc" "memory" (memory 0))
470
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
471
(func (export "core_record_error_export") (param f64) (result i32)
472
(local $retptr i32)
473
(local.set $retptr
474
(call $realloc
475
(i32.const 0)
476
(i32.const 0)
477
(i32.const 4)
478
(i32.const 16)))
479
(call $f (local.get 0) (local.get $retptr))
480
(local.get $retptr)
481
)
482
)
483
(core func $core_record_error
484
(canon lower (func $i "record-error") (memory $libc "memory") (realloc (func $libc "realloc")))
485
)
486
(core instance $i (instantiate $m
487
(with "" (instance (export "core_record_error" (func $core_record_error))))
488
(with "libc" (instance $libc))
489
))
490
(func $f_record_error
491
(param "a" f64)
492
(result (result f64 (error (record (field "line" u32) (field "col" u32)))))
493
(canon lift (core func $i "core_record_error_export") (memory $libc "memory"))
494
)
495
496
(component $nested
497
(import "f-e2" (type $f-e2 (eq $e2')))
498
(import "f" (func $f (param "a" f64) (result (result f64 (error $f-e2)))))
499
(export $e2 "e2" (type $e2'))
500
(export "record-error" (func $f) (func (param "a" f64) (result (result f64 (error $e2)))))
501
)
502
503
(instance (export "foo") (instantiate $nested
504
(with "f-e2" (type $e2'))
505
(with "f" (func $f_record_error))
506
))
507
)
508
"#
509
),
510
)?;
511
512
#[derive(Default)]
513
struct MyImports {}
514
515
impl imports::Host for MyImports {
516
fn convert_e2(&mut self, err: TrappableE2) -> anyhow::Result<imports::E2> {
517
match err {
518
TrappableE2::Normal(e) => Ok(e),
519
TrappableE2::Trap(e) => Err(e),
520
}
521
}
522
fn record_error(&mut self, a: f64) -> Result<f64, TrappableE2> {
523
if a == 0.0 {
524
Ok(a)
525
} else if a == 1.0 {
526
Err(imports::E2 {
527
line: 420,
528
col: 1312,
529
})?
530
} else {
531
Err(TrappableE2::Trap(anyhow!("record_error: trap")))
532
}
533
}
534
}
535
536
let mut linker = Linker::new(&engine);
537
imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
538
539
let mut store = Store::new(&engine, MyImports::default());
540
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
541
542
assert_eq!(
543
results
544
.foo()
545
.call_record_error(&mut store, 0.0)
546
.expect("no trap")
547
.expect("no error returned"),
548
0.0
549
);
550
551
let e = results
552
.foo()
553
.call_record_error(&mut store, 1.0)
554
.expect("no trap")
555
.err()
556
.expect("error returned");
557
assert!(matches!(
558
e,
559
record_error::foo::E2 {
560
line: 420,
561
col: 1312
562
}
563
));
564
565
let e = results
566
.foo()
567
.call_record_error(&mut store, 2.0)
568
.err()
569
.expect("trap");
570
assert_eq!(
571
format!("{}", e.source().expect("trap message is stored in source")),
572
"record_error: trap"
573
);
574
575
Ok(())
576
}
577
}
578
579
mod variant_error {
580
use super::*;
581
use exports::foo;
582
use inline::inline::imports;
583
use wasmtime::component::HasSelf;
584
585
wasmtime::component::bindgen!({
586
inline: "
587
package inline:inline;
588
interface imports {
589
enum e1 { a, b, c }
590
record e2 { line: u32, col: u32 }
591
variant e3 { E1(e1), E2(e2) }
592
variant-error: func(a: f64) -> result<f64, e3>;
593
}
594
world result-playground {
595
import imports;
596
export foo: interface {
597
enum e1 { a, b, c }
598
record e2 { line: u32, col: u32 }
599
variant e3 { E1(e1), E2(e2) }
600
variant-error: func(a: f64) -> result<f64, e3>;
601
}
602
}",
603
trappable_error_type: { "inline:inline/imports/e3" => TrappableE3 },
604
imports: { default: trappable },
605
});
606
607
pub enum TrappableE3 {
608
Normal(imports::E3),
609
Trap(anyhow::Error),
610
}
611
612
impl From<imports::E3> for TrappableE3 {
613
fn from(t: imports::E3) -> TrappableE3 {
614
TrappableE3::Normal(t)
615
}
616
}
617
618
#[test]
619
fn run() -> Result<(), Error> {
620
let engine = engine();
621
let component = Component::new(
622
&engine,
623
format!(
624
r#"
625
(component
626
(type $e1' (enum "a" "b" "c"))
627
(type $e2' (record (field "line" u32) (field "col" u32)))
628
(type $e3' (variant
629
(case "E1" $e1')
630
(case "E2" $e2')
631
))
632
(import (interface "inline:inline/imports") (instance $i
633
(export "e1" (type $e1 (eq $e1')))
634
(export "e2" (type $e2 (eq $e2')))
635
(type $e3' (variant
636
(case "E1" $e1)
637
(case "E2" $e2)
638
))
639
(export "e3" (type $e3 (eq $e3')))
640
(type $result (result f64 (error $e3)))
641
(export "variant-error" (func (param "a" f64) (result $result)))
642
))
643
(core module $libc
644
(memory (export "memory") 1)
645
{REALLOC_AND_FREE}
646
)
647
(core instance $libc (instantiate $libc))
648
(core module $m
649
(import "" "core_variant_error" (func $f (param f64 i32)))
650
(import "libc" "memory" (memory 0))
651
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
652
(func (export "core_variant_error_export") (param f64) (result i32)
653
(local $retptr i32)
654
(local.set $retptr
655
(call $realloc
656
(i32.const 0)
657
(i32.const 0)
658
(i32.const 4)
659
(i32.const 16)))
660
(call $f (local.get 0) (local.get $retptr))
661
(local.get $retptr)
662
)
663
)
664
(core func $core_variant_error
665
(canon lower (func $i "variant-error") (memory $libc "memory") (realloc (func $libc "realloc")))
666
)
667
(core instance $i (instantiate $m
668
(with "" (instance (export "core_variant_error" (func $core_variant_error))))
669
(with "libc" (instance $libc))
670
))
671
(func $f_variant_error
672
(param "a" f64)
673
(result (result f64 (error $e3')))
674
(canon lift (core func $i "core_variant_error_export") (memory $libc "memory"))
675
)
676
677
(component $nested
678
(import "f-e1" (type $e1i (eq $e1')))
679
(import "f-e2" (type $e2i (eq $e2')))
680
(type $e3i' (variant
681
(case "E1" $e1i)
682
(case "E2" $e2i)
683
))
684
(import "f-e3" (type $e3i (eq $e3i')))
685
(import "f" (func $f (param "a" f64) (result (result f64 (error $e3i)))))
686
(export $e1 "e1" (type $e1'))
687
(export $e2 "e2" (type $e2'))
688
(type $e3' (variant
689
(case "E1" $e1)
690
(case "E2" $e2)
691
))
692
(export $e3 "e3" (type $e3'))
693
(export "variant-error" (func $f)
694
(func (param "a" f64) (result (result f64 (error $e3)))))
695
)
696
697
(instance (export "foo") (instantiate $nested
698
(with "f-e1" (type $e1'))
699
(with "f-e2" (type $e2'))
700
(with "f-e3" (type $e3'))
701
(with "f" (func $f_variant_error))
702
))
703
)
704
"#
705
),
706
)?;
707
708
#[derive(Default)]
709
struct MyImports {}
710
711
impl imports::Host for MyImports {
712
fn convert_e3(&mut self, err: TrappableE3) -> anyhow::Result<imports::E3> {
713
match err {
714
TrappableE3::Normal(e) => Ok(e),
715
TrappableE3::Trap(e) => Err(e),
716
}
717
}
718
fn variant_error(&mut self, a: f64) -> Result<f64, TrappableE3> {
719
if a == 0.0 {
720
Ok(a)
721
} else if a == 1.0 {
722
Err(imports::E3::E2(imports::E2 {
723
line: 420,
724
col: 1312,
725
}))?
726
} else {
727
Err(TrappableE3::Trap(anyhow!("variant_error: trap")))
728
}
729
}
730
}
731
732
let mut linker = Linker::new(&engine);
733
imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
734
735
let mut store = Store::new(&engine, MyImports::default());
736
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
737
738
assert_eq!(
739
results
740
.foo()
741
.call_variant_error(&mut store, 0.0)
742
.expect("no trap")
743
.expect("no error returned"),
744
0.0
745
);
746
747
let e = results
748
.foo()
749
.call_variant_error(&mut store, 1.0)
750
.expect("no trap")
751
.err()
752
.expect("error returned");
753
assert!(matches!(
754
e,
755
variant_error::foo::E3::E2(variant_error::foo::E2 {
756
line: 420,
757
col: 1312
758
})
759
));
760
761
let e = results
762
.foo()
763
.call_variant_error(&mut store, 2.0)
764
.err()
765
.expect("trap");
766
assert_eq!(
767
format!("{}", e.source().expect("trap message is stored in source")),
768
"variant_error: trap"
769
);
770
771
Ok(())
772
}
773
}
774
775
mod multiple_interfaces_error {
776
use super::*;
777
use exports::foo;
778
use inline::inline::imports;
779
use inline::inline::types;
780
use wasmtime::component::HasSelf;
781
782
wasmtime::component::bindgen!({
783
inline: "
784
package inline:inline;
785
interface types {
786
enum e1 { a, b, c }
787
enum-error: func(a: f64) -> result<f64, e1>;
788
}
789
interface imports {
790
use types.{e1};
791
enum-error: func(a: f64) -> result<f64, e1>;
792
}
793
world result-playground {
794
import imports;
795
export foo: interface {
796
enum e1 { a, b, c }
797
enum-error: func(a: f64) -> result<f64, e1>;
798
}
799
}",
800
trappable_error_type: { "inline:inline/types/e1" => TrappableE1 },
801
imports: { default: trappable },
802
});
803
804
pub enum TrappableE1 {
805
Normal(types::E1),
806
Trap(anyhow::Error),
807
}
808
809
impl From<types::E1> for TrappableE1 {
810
fn from(t: imports::E1) -> TrappableE1 {
811
TrappableE1::Normal(t)
812
}
813
}
814
815
#[test]
816
fn run() -> Result<(), Error> {
817
let engine = engine();
818
// NOTE: this component doesn't make use of a types import, and relies instead on
819
// subtyping.
820
let component = Component::new(
821
&engine,
822
format!(
823
r#"
824
(component
825
(type $err' (enum "a" "b" "c"))
826
(import (interface "inline:inline/imports") (instance $i
827
(export "e1" (type $e1 (eq $err')))
828
(export "enum-error" (func (param "a" f64) (result (result f64 (error $e1)))))
829
))
830
(core module $libc
831
(memory (export "memory") 1)
832
{REALLOC_AND_FREE}
833
)
834
(core instance $libc (instantiate $libc))
835
(core module $m
836
(import "" "core_enum_error" (func $f (param f64 i32)))
837
(import "libc" "memory" (memory 0))
838
(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
839
(func (export "core_enum_error_export") (param f64) (result i32)
840
(local $retptr i32)
841
(local.set $retptr
842
(call $realloc
843
(i32.const 0)
844
(i32.const 0)
845
(i32.const 4)
846
(i32.const 16)))
847
(call $f (local.get 0) (local.get $retptr))
848
(local.get $retptr)
849
)
850
)
851
(core func $core_enum_error
852
(canon lower (func $i "enum-error") (memory $libc "memory") (realloc (func $libc "realloc")))
853
)
854
(core instance $i (instantiate $m
855
(with "" (instance (export "core_enum_error" (func $core_enum_error))))
856
(with "libc" (instance $libc))
857
))
858
(func $f_enum_error
859
(param "a" f64)
860
(result (result f64 (error $err')))
861
(canon lift (core func $i "core_enum_error_export") (memory $libc "memory"))
862
)
863
864
(component $nested
865
(import "f-err" (type $err (eq $err')))
866
(import "f" (func $f (param "a" f64) (result (result f64 (error $err)))))
867
(export $err2 "err" (type $err'))
868
(export "enum-error" (func $f) (func (param "a" f64) (result (result f64 (error $err2)))))
869
)
870
871
(instance $n (instantiate $nested
872
(with "f-err" (type $err'))
873
(with "f" (func $f_enum_error))
874
))
875
(export "foo" (instance $n))
876
)
877
"#
878
),
879
)?;
880
881
// You can create concrete trap types which make it all the way out to the
882
// host caller, via downcast_ref below.
883
#[derive(Debug)]
884
struct MyTrap;
885
886
impl std::fmt::Display for MyTrap {
887
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
888
write!(f, "{self:?}")
889
}
890
}
891
impl std::error::Error for MyTrap {}
892
893
// It is possible to define From impls that target these generated trappable
894
// types. This allows you to integrate libraries with other error types, or
895
// use your own more descriptive error types, and use ? to convert them at
896
// their throw site.
897
impl From<MyTrap> for TrappableE1 {
898
fn from(t: MyTrap) -> TrappableE1 {
899
TrappableE1::Trap(anyhow!(t))
900
}
901
}
902
903
#[derive(Default)]
904
struct MyImports {}
905
906
impl types::Host for MyImports {
907
fn convert_e1(&mut self, err: TrappableE1) -> anyhow::Result<imports::E1> {
908
match err {
909
TrappableE1::Normal(e) => Ok(e),
910
TrappableE1::Trap(e) => Err(e),
911
}
912
}
913
fn enum_error(&mut self, a: f64) -> Result<f64, TrappableE1> {
914
if a == 0.0 {
915
Ok(a)
916
} else if a == 1.0 {
917
Err(imports::E1::A)?
918
} else {
919
Err(MyTrap)?
920
}
921
}
922
}
923
924
impl imports::Host for MyImports {
925
fn enum_error(&mut self, a: f64) -> Result<f64, TrappableE1> {
926
if a == 0.0 {
927
Ok(a)
928
} else if a == 1.0 {
929
Err(imports::E1::A)?
930
} else {
931
Err(MyTrap)?
932
}
933
}
934
}
935
936
let mut linker = Linker::new(&engine);
937
imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
938
939
let mut store = Store::new(&engine, MyImports::default());
940
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
941
942
assert_eq!(
943
results
944
.foo()
945
.call_enum_error(&mut store, 0.0)
946
.expect("no trap")
947
.expect("no error returned"),
948
0.0
949
);
950
951
let e = results
952
.foo()
953
.call_enum_error(&mut store, 1.0)
954
.expect("no trap")
955
.err()
956
.expect("error returned");
957
assert_eq!(e, foo::E1::A);
958
959
let e = results
960
.foo()
961
.call_enum_error(&mut store, 2.0)
962
.err()
963
.expect("trap");
964
assert_eq!(
965
format!("{}", e.source().expect("trap message is stored in source")),
966
"MyTrap"
967
);
968
e.downcast_ref::<MyTrap>()
969
.expect("downcast trap to concrete MyTrap type");
970
971
Ok(())
972
}
973
}
974
975
mod with_remapping {
976
use super::*;
977
use wasmtime::component::HasSelf;
978
979
mod interfaces {
980
wasmtime::component::bindgen!({
981
interfaces: "
982
import imports: interface {
983
empty-error: func(a: f64) -> result<f64>;
984
}",
985
imports: { default: trappable },
986
});
987
}
988
989
wasmtime::component::bindgen!({
990
inline: "
991
package inline:inline;
992
world result-playground {
993
import imports: interface {
994
empty-error: func(a: f64) -> result<f64>;
995
}
996
997
export empty-error: func(a: f64) -> result<f64>;
998
}",
999
with: {
1000
"imports": interfaces::imports,
1001
},
1002
imports: { default: trappable },
1003
});
1004
1005
#[test]
1006
fn run() -> Result<(), Error> {
1007
let engine = engine();
1008
let component = Component::new(
1009
&engine,
1010
r#"
1011
(component
1012
(import "imports" (instance $i
1013
(export "empty-error" (func (param "a" f64) (result (result f64))))
1014
))
1015
(core module $libc
1016
(memory (export "memory") 1)
1017
)
1018
(core instance $libc (instantiate $libc))
1019
(core module $m
1020
(import "" "core_empty_error" (func $f (param f64 i32)))
1021
(import "libc" "memory" (memory 0))
1022
(func (export "core_empty_error_export") (param f64) (result i32)
1023
(call $f (local.get 0) (i32.const 8))
1024
(i32.const 8)
1025
)
1026
)
1027
(core func $core_empty_error
1028
(canon lower (func $i "empty-error") (memory $libc "memory"))
1029
)
1030
(core instance $i (instantiate $m
1031
(with "" (instance (export "core_empty_error" (func $core_empty_error))))
1032
(with "libc" (instance $libc))
1033
))
1034
(func $f_empty_error
1035
(export "empty-error")
1036
(param "a" f64)
1037
(result (result f64))
1038
(canon lift (core func $i "core_empty_error_export") (memory $libc "memory"))
1039
)
1040
)
1041
"#,
1042
)?;
1043
1044
#[derive(Default)]
1045
struct MyImports {}
1046
1047
impl interfaces::imports::Host for MyImports {
1048
fn empty_error(&mut self, a: f64) -> Result<Result<f64, ()>, Error> {
1049
if a == 0.0 {
1050
Ok(Ok(a))
1051
} else if a == 1.0 {
1052
Ok(Err(()))
1053
} else {
1054
Err(anyhow!("empty_error: trap"))
1055
}
1056
}
1057
}
1058
1059
let mut linker = Linker::new(&engine);
1060
interfaces::imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;
1061
1062
let mut store = Store::new(&engine, MyImports::default());
1063
let results = ResultPlayground::instantiate(&mut store, &component, &linker)?;
1064
1065
assert_eq!(
1066
results
1067
.call_empty_error(&mut store, 0.0)
1068
.expect("no trap")
1069
.expect("no error returned"),
1070
0.0
1071
);
1072
1073
results
1074
.call_empty_error(&mut store, 1.0)
1075
.expect("no trap")
1076
.err()
1077
.expect("() error returned");
1078
1079
let e = results
1080
.call_empty_error(&mut store, 2.0)
1081
.err()
1082
.expect("trap");
1083
assert_eq!(
1084
format!("{}", e.source().expect("trap message is stored in source")),
1085
"empty_error: trap"
1086
);
1087
1088
Ok(())
1089
}
1090
}
1091
1092