Coverage Report

Created: 2026-07-29 12:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/src/execution/runtime_structure/values.rs
Line
Count
Source
1
use core::{
2
    fmt::{Debug, Display},
3
    ops::{Add, Div, Mul, Sub},
4
};
5
6
use crate::{FuncAddr, NumType, RefType, ValType};
7
8
#[derive(Clone, Debug, Copy, PartialOrd)]
9
#[repr(transparent)]
10
pub struct F32(pub f32);
11
12
impl Display for F32 {
13
10
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14
10
        write!(f, "{}", self.0)
15
10
    }
16
}
17
18
impl PartialEq for F32 {
19
5.22k
    fn eq(&self, other: &Self) -> bool {
20
5.22k
        self.0.eq(&other.0)
21
5.22k
    }
22
}
23
24
impl Add for F32 {
25
    type Output = Self;
26
14.4k
    fn add(self, rhs: Self) -> Self::Output {
27
14.4k
        Self(self.0 + rhs.0)
28
14.4k
    }
29
}
30
31
impl Sub for F32 {
32
    type Output = Self;
33
25.5k
    fn sub(self, rhs: Self) -> Self::Output {
34
25.5k
        Self(self.0 - rhs.0)
35
25.5k
    }
36
}
37
38
impl Mul for F32 {
39
    type Output = Self;
40
2.35k
    fn mul(self, rhs: Self) -> Self::Output {
41
2.35k
        Self(self.0 * rhs.0)
42
2.35k
    }
43
}
44
45
impl Div for F32 {
46
    type Output = Self;
47
2.34k
    fn div(self, rhs: Self) -> Self::Output {
48
2.34k
        Self(self.0 / rhs.0)
49
2.34k
    }
50
}
51
52
impl F32 {
53
132
    pub fn abs(&self) -> Self {
54
132
        Self(libm::fabsf(self.0))
55
132
    }
56
277
    pub fn neg(&self) -> Self {
57
277
        Self(-self.0)
58
277
    }
59
205
    pub fn ceil(&self) -> Self {
60
205
        if self.0.is_nan() {
61
20
            return Self(f32::NAN);
62
185
        }
63
185
        Self(libm::ceilf(self.0))
64
205
    }
65
204
    pub fn floor(&self) -> Self {
66
204
        if self.0.is_nan() {
67
20
            return Self(f32::NAN);
68
184
        }
69
184
        Self(libm::floorf(self.0))
70
204
    }
71
201
    pub fn trunc(&self) -> Self {
72
201
        if self.0.is_nan() {
73
20
            return Self(f32::NAN);
74
181
        }
75
181
        Self(libm::truncf(self.0))
76
201
    }
77
209
    pub fn nearest(&self) -> Self {
78
209
        if self.0.is_nan() {
79
20
            return Self(f32::NAN);
80
189
        }
81
189
        Self(libm::rintf(self.0))
82
209
    }
83
2
    pub fn round(&self) -> Self {
84
2
        Self(libm::roundf(self.0))
85
2
    }
86
324
    pub fn sqrt(&self) -> Self {
87
324
        Self(libm::sqrtf(self.0))
88
324
    }
89
90
1.92k
    pub fn min(&self, rhs: Self) -> Self {
91
1.92k
        Self(if self.0.is_nan() || 
rhs.01.51k
.
is_nan1.51k
() {
92
471
            f32::NAN
93
1.45k
        } else if self.0 == 0.0 && 
rhs.0 == 0.0182
{
94
39
            if self.to_bits() >> 31 == 1 {
95
16
                self.0
96
            } else {
97
23
                rhs.0
98
            }
99
        } else {
100
1.41k
            self.0.min(rhs.0)
101
        })
102
1.92k
    }
103
1.91k
    pub fn max(&self, rhs: Self) -> Self {
104
1.91k
        Self(if self.0.is_nan() || 
rhs.01.50k
.
is_nan1.50k
() {
105
471
            f32::NAN
106
1.44k
        } else if self.0 == 0.0 && 
rhs.0 == 0.0182
{
107
39
            if self.to_bits() >> 31 == 1 {
108
16
                rhs.0
109
            } else {
110
23
                self.0
111
            }
112
        } else {
113
1.40k
            self.0.max(rhs.0)
114
        })
115
1.91k
    }
116
333
    pub fn copysign(&self, rhs: Self) -> Self {
117
333
        Self(libm::copysignf(self.0, rhs.0))
118
333
    }
119
57.5k
    pub fn from_bits(other: u32) -> Self {
120
57.5k
        Self(f32::from_bits(other))
121
57.5k
    }
122
754
    pub fn is_nan(&self) -> bool {
123
754
        self.0.is_nan()
124
754
    }
125
698
    pub fn is_infinity(&self) -> bool {
126
698
        self.0.is_infinite()
127
698
    }
128
524
    pub fn is_negative_infinity(&self) -> bool {
129
524
        self.0.is_infinite() && 
self.0 < 0.032
130
524
    }
131
132
255
    pub fn as_i32(&self) -> i32 {
133
255
        self.0 as i32
134
255
    }
135
243
    pub fn as_u32(&self) -> u32 {
136
243
        self.0 as u32
137
243
    }
138
72
    pub fn as_i64(&self) -> i64 {
139
72
        self.0 as i64
140
72
    }
141
52
    pub fn as_u64(&self) -> u64 {
142
52
        self.0 as u64
143
52
    }
144
127
    pub fn as_f64(&self) -> F64 {
145
127
        F64(self.0 as f64)
146
127
    }
147
87
    pub fn reinterpret_as_i32(&self) -> i32 {
148
87
        self.0.to_bits() as i32
149
87
    }
150
31.3k
    pub fn to_bits(&self) -> u32 {
151
31.3k
        self.0.to_bits()
152
31.3k
    }
153
}
154
155
#[derive(Clone, Debug, Copy, PartialOrd)]
156
#[repr(transparent)]
157
pub struct F64(pub f64);
158
159
impl Display for F64 {
160
10
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161
10
        write!(f, "{}", self.0)
162
10
    }
163
}
164
165
impl PartialEq for F64 {
166
2.95k
    fn eq(&self, other: &Self) -> bool {
167
2.95k
        self.0.eq(&other.0)
168
2.95k
    }
169
}
170
171
impl Add for F64 {
172
    type Output = Self;
173
13.3k
    fn add(self, rhs: Self) -> Self::Output {
174
13.3k
        Self(self.0 + rhs.0)
175
13.3k
    }
176
}
177
178
impl Sub for F64 {
179
    type Output = Self;
180
24.2k
    fn sub(self, rhs: Self) -> Self::Output {
181
24.2k
        Self(self.0 - rhs.0)
182
24.2k
    }
183
}
184
185
impl Mul for F64 {
186
    type Output = Self;
187
1.62k
    fn mul(self, rhs: Self) -> Self::Output {
188
1.62k
        Self(self.0 * rhs.0)
189
1.62k
    }
190
}
191
192
impl Div for F64 {
193
    type Output = Self;
194
1.55k
    fn div(self, rhs: Self) -> Self::Output {
195
1.55k
        Self(self.0 / rhs.0)
196
1.55k
    }
197
}
198
199
impl F64 {
200
78
    pub fn abs(&self) -> Self {
201
78
        Self(libm::fabs(self.0))
202
78
    }
203
182
    pub fn neg(&self) -> Self {
204
182
        Self(-self.0)
205
182
    }
206
117
    pub fn ceil(&self) -> Self {
207
117
        if self.0.is_nan() {
208
12
            return Self(f64::NAN);
209
105
        }
210
105
        Self(libm::ceil(self.0))
211
117
    }
212
116
    pub fn floor(&self) -> Self {
213
116
        if self.0.is_nan() {
214
12
            return Self(f64::NAN);
215
104
        }
216
104
        Self(libm::floor(self.0))
217
116
    }
218
113
    pub fn trunc(&self) -> Self {
219
113
        if self.0.is_nan() {
220
12
            return Self(f64::NAN);
221
101
        }
222
101
        Self(libm::trunc(self.0))
223
113
    }
224
121
    pub fn nearest(&self) -> Self {
225
121
        if self.0.is_nan() {
226
12
            return Self(f64::NAN);
227
109
        }
228
109
        Self(libm::rint(self.0))
229
121
    }
230
2
    pub fn round(&self) -> Self {
231
2
        Self(libm::round(self.0))
232
2
    }
233
222
    pub fn sqrt(&self) -> Self {
234
222
        Self(libm::sqrt(self.0))
235
222
    }
236
237
1.18k
    pub fn min(&self, rhs: Self) -> Self {
238
1.18k
        Self(if self.0.is_nan() || 
rhs.0938
.
is_nan938
() {
239
310
            f64::NAN
240
872
        } else if self.0 == 0.0 && 
rhs.0 == 0.0114
{
241
28
            if self.to_bits() >> 63 == 1 {
242
10
                self.0
243
            } else {
244
18
                rhs.0
245
            }
246
        } else {
247
844
            self.0.min(rhs.0)
248
        })
249
1.18k
    }
250
1.18k
    pub fn max(&self, rhs: Self) -> Self {
251
1.18k
        Self(if self.0.is_nan() || 
rhs.0938
.
is_nan938
() {
252
310
            f64::NAN
253
872
        } else if self.0 == 0.0 && 
rhs.0 == 0.0114
{
254
28
            if self.to_bits() >> 63 == 1 {
255
10
                rhs.0
256
            } else {
257
18
                self.0
258
            }
259
        } else {
260
844
            self.0.max(rhs.0)
261
        })
262
1.18k
    }
263
333
    pub fn copysign(&self, rhs: Self) -> Self {
264
333
        Self(libm::copysign(self.0, rhs.0))
265
333
    }
266
267
56.2k
    pub fn from_bits(other: u64) -> Self {
268
56.2k
        Self(f64::from_bits(other))
269
56.2k
    }
270
833
    pub fn is_nan(&self) -> bool {
271
833
        self.0.is_nan()
272
833
    }
273
549
    pub fn is_infinity(&self) -> bool {
274
549
        self.0.is_infinite()
275
549
    }
276
345
    pub fn is_negative_infinity(&self) -> bool {
277
345
        self.0.is_infinite() && 
self.0 < 0.024
278
345
    }
279
280
158
    pub fn as_i32(&self) -> i32 {
281
158
        self.0 as i32
282
158
    }
283
164
    pub fn as_u32(&self) -> u32 {
284
164
        self.0 as u32
285
164
    }
286
77
    pub fn as_i64(&self) -> i64 {
287
77
        self.0 as i64
288
77
    }
289
72
    pub fn as_u64(&self) -> u64 {
290
72
        self.0 as u64
291
72
    }
292
264
    pub fn as_f32(&self) -> F32 {
293
264
        F32(self.0 as f32)
294
264
    }
295
86
    pub fn reinterpret_as_i64(&self) -> i64 {
296
86
        self.0.to_bits() as i64
297
86
    }
298
17.6k
    pub fn to_bits(&self) -> u64 {
299
17.6k
        self.0.to_bits()
300
17.6k
    }
301
}
302
303
/// A value at runtime. This is essentially a duplicate of [ValType] just with additional values.
304
///
305
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#values>
306
// TODO implement missing variants
307
#[derive(Clone, Copy, Debug, PartialEq)]
308
pub enum Value {
309
    I32(u32),
310
    I64(u64),
311
    F32(F32),
312
    F64(F64),
313
    V128([u8; 16]),
314
    Ref(Ref),
315
}
316
317
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318
pub enum Ref {
319
    Null(RefType),
320
    Func(FuncAddr),
321
    Extern(ExternAddr),
322
}
323
324
impl Display for Ref {
325
4
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326
4
        match self {
327
1
            Ref::Func(func_addr) => write!(f, "FuncRef({func_addr:?})"),
328
1
            Ref::Extern(extern_addr) => write!(f, "ExternRef({extern_addr:?})"),
329
2
            Ref::Null(ty) => write!(f, "Null({ty:?})"),
330
        }
331
4
    }
332
}
333
334
impl Ref {
335
146
    pub fn ty(self) -> RefType {
336
146
        match self {
337
145
            Ref::Null(ref_type) => ref_type,
338
0
            Ref::Func(_) => RefType::FuncRef,
339
1
            Ref::Extern(_) => RefType::ExternRef,
340
        }
341
146
    }
342
}
343
344
/// The WebAssembly specification defines an externaddr as an address to an
345
/// "external" type, i.e. is a type which is managed by the embedder. For this
346
/// interpreter the task of managing external objects and relating them to
347
/// addresses is handed off to the user, which means that an [`ExternAddr`] can
348
/// simply be seen as an integer that is opaque to Wasm code without any meaning
349
/// assigned to it.
350
///
351
/// See: WebAssembly Specification 2.0 - 2.3.3, 4.2.1
352
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353
pub struct ExternAddr(pub usize);
354
355
impl Value {
356
645k
    pub fn default_from_ty(ty: ValType) -> Self {
357
645k
        match ty {
358
157
            ValType::NumType(NumType::I32) => Self::I32(0),
359
645k
            ValType::NumType(NumType::I64) => Self::I64(0),
360
83
            ValType::NumType(NumType::F32) => Self::F32(F32(0.0)),
361
56
            ValType::NumType(NumType::F64) => Self::F64(F64(0.0_f64)),
362
2
            ValType::RefType(ref_type) => Self::Ref(Ref::Null(ref_type)),
363
19
            ValType::VecType => Self::V128([0; 16]),
364
        }
365
645k
    }
366
367
88.6k
    pub fn to_ty(&self) -> ValType {
368
63
        match self {
369
17.0k
            Value::I32(_) => ValType::NumType(NumType::I32),
370
1.94k
            Value::I64(_) => ValType::NumType(NumType::I64),
371
12.2k
            Value::F32(_) => ValType::NumType(NumType::F32),
372
12.4k
            Value::F64(_) => ValType::NumType(NumType::F64),
373
18
            Value::Ref(Ref::Null(ref_type)) => ValType::RefType(*ref_type),
374
0
            Value::Ref(Ref::Func(_)) => ValType::RefType(RefType::FuncRef),
375
45
            Value::Ref(Ref::Extern(_)) => ValType::RefType(RefType::ExternRef),
376
44.8k
            Value::V128(_) => ValType::VecType,
377
        }
378
88.6k
    }
379
}
380
381
/// An error used in all [`TryFrom<Value>`] implementations for Rust types ([`i32`], [`F32`], [`Ref`], ...)
382
#[derive(Debug, PartialEq, Eq)]
383
pub struct ValueTypeMismatchError;
384
385
impl Display for ValueTypeMismatchError {
386
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
387
0
        f.write_str("failed to convert Value to a Rust value because the types did not match")
388
0
    }
389
}
390
391
impl From<u32> for Value {
392
14
    fn from(x: u32) -> Self {
393
14
        Value::I32(x)
394
14
    }
395
}
396
impl TryFrom<Value> for u32 {
397
    type Error = ValueTypeMismatchError;
398
399
1.03M
    fn try_from(value: Value) -> Result<Self, Self::Error> {
400
1.03M
        match value {
401
1.03M
            Value::I32(x) => Ok(x),
402
0
            _ => Err(ValueTypeMismatchError),
403
        }
404
1.03M
    }
405
}
406
407
impl From<i32> for Value {
408
4.80M
    fn from(x: i32) -> Self {
409
4.80M
        Value::I32(x as u32)
410
4.80M
    }
411
}
412
impl TryFrom<Value> for i32 {
413
    type Error = ValueTypeMismatchError;
414
415
8.78M
    fn try_from(value: Value) -> Result<Self, Self::Error> {
416
8.78M
        match value {
417
8.78M
            Value::I32(x) => Ok(x as i32),
418
0
            _ => Err(ValueTypeMismatchError),
419
        }
420
8.78M
    }
421
}
422
423
impl From<u64> for Value {
424
72
    fn from(x: u64) -> Self {
425
72
        Value::I64(x)
426
72
    }
427
}
428
impl TryFrom<Value> for u64 {
429
    type Error = ValueTypeMismatchError;
430
431
106
    fn try_from(value: Value) -> Result<Self, Self::Error> {
432
106
        match value {
433
106
            Value::I64(x) => Ok(x),
434
0
            _ => Err(ValueTypeMismatchError),
435
        }
436
106
    }
437
}
438
impl From<i64> for Value {
439
191k
    fn from(x: i64) -> Self {
440
191k
        Value::I64(x as u64)
441
191k
    }
442
}
443
impl TryFrom<Value> for i64 {
444
    type Error = ValueTypeMismatchError;
445
446
241k
    fn try_from(value: Value) -> Result<Self, Self::Error> {
447
241k
        match value {
448
241k
            Value::I64(x) => Ok(x as i64),
449
0
            _ => Err(ValueTypeMismatchError),
450
        }
451
241k
    }
452
}
453
454
impl From<F32> for Value {
455
97.1k
    fn from(x: F32) -> Self {
456
97.1k
        Value::F32(x)
457
97.1k
    }
458
}
459
impl TryFrom<Value> for F32 {
460
    type Error = ValueTypeMismatchError;
461
462
130k
    fn try_from(value: Value) -> Result<Self, Self::Error> {
463
130k
        match value {
464
130k
            Value::F32(x) => Ok(x),
465
0
            _ => Err(ValueTypeMismatchError),
466
        }
467
130k
    }
468
}
469
470
impl From<F64> for Value {
471
95.3k
    fn from(x: F64) -> Self {
472
95.3k
        Value::F64(x)
473
95.3k
    }
474
}
475
impl TryFrom<Value> for F64 {
476
    type Error = ValueTypeMismatchError;
477
478
127k
    fn try_from(value: Value) -> Result<Self, Self::Error> {
479
127k
        match value {
480
127k
            Value::F64(x) => Ok(x),
481
0
            _ => Err(ValueTypeMismatchError),
482
        }
483
127k
    }
484
}
485
486
impl From<[u8; 16]> for Value {
487
530
    fn from(value: [u8; 16]) -> Self {
488
530
        Value::V128(value)
489
530
    }
490
}
491
impl TryFrom<Value> for [u8; 16] {
492
    type Error = ValueTypeMismatchError;
493
494
46.3k
    fn try_from(value: Value) -> Result<Self, Self::Error> {
495
46.3k
        match value {
496
46.3k
            Value::V128(x) => Ok(x),
497
0
            _ => Err(ValueTypeMismatchError),
498
        }
499
46.3k
    }
500
}
501
502
impl From<Ref> for Value {
503
135
    fn from(value: Ref) -> Self {
504
135
        Self::Ref(value)
505
135
    }
506
}
507
508
impl TryFrom<Value> for Ref {
509
    type Error = ValueTypeMismatchError;
510
511
1.25k
    fn try_from(value: Value) -> Result<Self, Self::Error> {
512
1.25k
        match value {
513
1.25k
            Value::Ref(rref) => Ok(rref),
514
0
            _ => Err(ValueTypeMismatchError),
515
        }
516
1.25k
    }
517
}
518
519
impl From<f32> for Value {
520
0
    fn from(value: f32) -> Self {
521
0
        F32(value).into()
522
0
    }
523
}
524
525
impl TryFrom<Value> for f32 {
526
    type Error = ValueTypeMismatchError;
527
528
0
    fn try_from(value: Value) -> Result<Self, Self::Error> {
529
0
        F32::try_from(value).map(|f| f.0)
530
0
    }
531
}
532
533
impl From<f64> for Value {
534
0
    fn from(value: f64) -> Self {
535
0
        F64(value).into()
536
0
    }
537
}
538
539
impl TryFrom<Value> for f64 {
540
    type Error = ValueTypeMismatchError;
541
542
0
    fn try_from(value: Value) -> Result<Self, Self::Error> {
543
0
        F64::try_from(value).map(|f| f.0)
544
0
    }
545
}
546
547
#[cfg(test)]
548
mod test {
549
    use alloc::string::ToString;
550
551
    use crate::{Addr, ExternAddr, RefType, F32, F64};
552
553
    use super::{FuncAddr, Ref};
554
555
    #[test]
556
1
    fn rounding_f32() {
557
1
        let round_towards_0_f32 = F32(0.5 - f32::EPSILON).round();
558
1
        let round_towards_1_f32 = F32(0.5 + f32::EPSILON).round();
559
560
1
        assert_eq!(round_towards_0_f32, F32(0.0));
561
1
        assert_eq!(round_towards_1_f32, F32(1.0));
562
1
    }
563
564
    #[test]
565
1
    fn rounding_f64() {
566
1
        let round_towards_0_f64 = F64(0.5 - f64::EPSILON).round();
567
1
        let round_towards_1_f64 = F64(0.5 + f64::EPSILON).round();
568
569
1
        assert_eq!(round_towards_0_f64, F64(0.0));
570
1
        assert_eq!(round_towards_1_f64, F64(1.0));
571
1
    }
572
573
    #[test]
574
1
    fn display_f32() {
575
10
        for x in [
576
1
            -1.0,
577
1
            0.0,
578
1
            1.0,
579
1
            13.3,
580
1
            f32::INFINITY,
581
1
            f32::MAX,
582
1
            f32::MIN,
583
1
            f32::NAN,
584
1
            f32::NEG_INFINITY,
585
1
            core::f32::consts::PI,
586
1
        ] {
587
10
            let wrapped = F32(x).to_string();
588
10
            let expected = x.to_string();
589
10
            assert_eq!(wrapped, expected);
590
        }
591
1
    }
592
593
    #[test]
594
1
    fn display_f64() {
595
10
        for x in [
596
1
            -1.0,
597
1
            0.0,
598
1
            1.0,
599
1
            13.3,
600
1
            f64::INFINITY,
601
1
            f64::MAX,
602
1
            f64::MIN,
603
1
            f64::NAN,
604
1
            f64::NEG_INFINITY,
605
1
            core::f64::consts::PI,
606
1
        ] {
607
10
            let wrapped = F64(x).to_string();
608
10
            let expected = x.to_string();
609
10
            assert_eq!(wrapped, expected);
610
        }
611
1
    }
612
613
    #[test]
614
1
    fn display_ref() {
615
1
        assert_eq!(
616
1
            Ref::Func(FuncAddr::new(11)).to_string(),
617
            "FuncRef(FuncAddr(11))"
618
        );
619
1
        assert_eq!(
620
1
            Ref::Extern(ExternAddr(13)).to_string(),
621
            "ExternRef(ExternAddr(13))"
622
        );
623
1
        assert_eq!(Ref::Null(RefType::FuncRef).to_string(), "Null(FuncRef)");
624
1
        assert_eq!(Ref::Null(RefType::ExternRef).to_string(), "Null(ExternRef)");
625
1
    }
626
}