1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use crate::{
    module,
    module::{ConstExpr, ElementSegmentItems},
    store::Stored,
    AsContext,
    AsContextMut,
};
use wasmi_arena::ArenaIndex;
use wasmi_core::ValueType;

/// A raw index to a element segment entity.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ElementSegmentIdx(u32);

impl ArenaIndex for ElementSegmentIdx {
    fn into_usize(self) -> usize {
        self.0 as usize
    }

    fn from_usize(value: usize) -> Self {
        let value = value.try_into().unwrap_or_else(|error| {
            panic!("index {value} is out of bounds as element segment index: {error}")
        });
        Self(value)
    }
}

/// A Wasm data segment reference.
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct ElementSegment(Stored<ElementSegmentIdx>);

impl ElementSegment {
    /// Creates a new linear memory reference.
    pub fn from_inner(stored: Stored<ElementSegmentIdx>) -> Self {
        Self(stored)
    }

    /// Returns the underlying stored representation.
    pub fn as_inner(&self) -> &Stored<ElementSegmentIdx> {
        &self.0
    }

    /// Allocates a new [`ElementSegment`] on the store.
    ///
    /// # Errors
    ///
    /// If more than [`u32::MAX`] much linear memory is allocated.
    pub fn new(mut ctx: impl AsContextMut, segment: &module::ElementSegment) -> Self {
        let entity = ElementSegmentEntity::from(segment);
        ctx.as_context_mut()
            .store
            .inner
            .alloc_element_segment(entity)
    }

    /// Returns the number of items in the [`ElementSegment`].
    pub fn size(&self, ctx: impl AsContext) -> u32 {
        ctx.as_context()
            .store
            .inner
            .resolve_element_segment(self)
            .size()
    }

    /// Drops the items of the [`ElementSegment`].
    pub fn drop_items(&self, mut ctx: impl AsContextMut) {
        ctx.as_context_mut()
            .store
            .inner
            .resolve_element_segment_mut(self)
            .drop_items()
    }
}

/// An instantiated [`ElementSegmentEntity`].
///
/// # Note
///
/// With the `bulk-memory` Wasm proposal it is possible to interact
/// with element segments at runtime. Therefore Wasm instances now have
/// a need to have an instantiated representation of data segments.
#[derive(Debug)]
pub struct ElementSegmentEntity {
    /// The [`ValueType`] of elements of this [`ElementSegmentEntity`].
    ty: ValueType,
    /// The underlying items of the instance element segment.
    ///
    /// # Note
    ///
    /// These items are just readable after instantiation.
    /// Using Wasm `elem.drop` simply replaces the instance
    /// with an empty one.
    items: Option<ElementSegmentItems>,
}

impl From<&'_ module::ElementSegment> for ElementSegmentEntity {
    fn from(segment: &'_ module::ElementSegment) -> Self {
        let ty = segment.ty();
        match segment.kind() {
            module::ElementSegmentKind::Passive | module::ElementSegmentKind::Active(_) => Self {
                ty,
                items: Some(segment.items_cloned()),
            },
            module::ElementSegmentKind::Declared => Self::empty(ty),
        }
    }
}

impl ElementSegmentEntity {
    /// Create an empty [`ElementSegmentEntity`] representing dropped element segments.
    fn empty(ty: ValueType) -> Self {
        Self { ty, items: None }
    }

    /// Returns the [`ValueType`] of elements in the [`ElementSegmentEntity`].
    pub fn ty(&self) -> ValueType {
        self.ty
    }

    /// Returns the number of items in the [`ElementSegment`].
    pub fn size(&self) -> u32 {
        self.items().len() as u32
    }

    /// Returns the items of the [`ElementSegmentEntity`].
    pub fn items(&self) -> &[ConstExpr] {
        self.items
            .as_ref()
            .map(ElementSegmentItems::items)
            .unwrap_or(&[])
    }

    /// Drops the items of the [`ElementSegmentEntity`].
    pub fn drop_items(&mut self) {
        self.items = None;
    }
}