1use crate::globals::Globals;
2use crate::trap::{Result, Trap, TrapReason};
3use crate::value::LittleEndian;
4use std::any;
5use std::mem::size_of;
6use wain_ast as ast;
7
8const PAGE_SIZE: usize = 65536; const MAX_MEMORY_BYTES: usize = u32::MAX as usize; pub struct Memory {
16 max: Option<u32>,
17 data: Vec<u8>,
18}
19
20impl Memory {
21 pub fn allocate(memories: &[ast::Memory]) -> Result<Self> {
23 assert!(memories.len() <= 1);
25 if let Some(memory) = memories.get(0) {
26 if let Some(i) = &memory.import {
27 Err(Trap::unknown_import(i, "memory", memory.start))
28 } else {
29 let (min, max) = match &memory.ty.limit {
30 ast::Limits::Range(min, max) => (*min, Some(*max)),
31 ast::Limits::From(min) => (*min, None),
32 };
33 let data = if min == 0 {
34 vec![]
35 } else {
36 let len = (min as usize) * PAGE_SIZE;
37 vec![0; len]
38 };
39 Ok(Self { max, data })
40 }
41 } else {
42 Ok(Self {
44 max: Some(0),
45 data: vec![],
46 })
47 }
48 }
49
50 pub fn new_data(&mut self, segment: &ast::DataSegment, globals: &Globals) -> Result<()> {
52 let offset = match &segment.offset[segment.offset.len() - 1].kind {
55 ast::InsnKind::GlobalGet(idx) => globals.get(*idx),
56 ast::InsnKind::I32Const(i) => *i,
57 _ => unreachable!("unexpected instruction for element offset"),
58 };
59 let offset = offset as usize;
60 let data = &segment.data;
61 let end_addr = offset + data.len();
62
63 if let Some(max) = self.max {
64 let max = max as usize;
65 if end_addr > max * PAGE_SIZE {
66 return Err(Trap::new(
67 TrapReason::OutOfLimit {
68 max,
69 idx: end_addr,
70 kind: "data element",
71 },
72 segment.start,
73 ));
74 }
75 }
76
77 if self.data.len() < end_addr {
78 return Err(Trap::new(
79 TrapReason::DataSegmentOutOfBuffer {
80 segment_end: end_addr,
81 buffer_size: self.data.len(),
82 },
83 segment.start,
84 ));
85 }
86
87 #[allow(clippy::manual_memcpy)]
89 {
90 for i in 0..data.len() {
91 self.data[offset + i] = data[i];
92 }
93 }
94
95 Ok(())
96 }
97
98 pub fn size(&self) -> u32 {
99 (self.data.len() / PAGE_SIZE) as u32
100 }
101
102 pub fn grow(&mut self, num_pages: u32) -> i32 {
103 let prev = self.size();
105 let (next, overflow) = prev.overflowing_add(num_pages);
106 if overflow {
107 return -1;
108 }
109 if let Some(max) = self.max {
110 if next > max {
111 return -1;
112 }
113 }
114 let next_len = (next as usize) * PAGE_SIZE;
115 if next_len > MAX_MEMORY_BYTES {
116 return -1;
121 }
122 self.data.resize(next_len, 0);
123 prev as i32
124 }
125
126 fn check_addr<V: LittleEndian>(&self, addr: usize, at: usize, operation: &'static str) -> Result<()> {
127 if addr.saturating_add(size_of::<V>()) > self.data.len() {
128 Err(Trap::new(
129 TrapReason::LoadMemoryOutOfRange {
130 max: self.data.len(),
131 addr,
132 operation,
133 ty: any::type_name::<V>(),
134 },
135 at,
136 ))
137 } else {
138 Ok(())
139 }
140 }
141
142 pub fn load<V: LittleEndian>(&self, addr: usize, at: usize) -> Result<V> {
144 self.check_addr::<V>(addr, at, "load")?;
145 Ok(LittleEndian::read(&self.data, addr))
146 }
147
148 pub fn store<V: LittleEndian>(&mut self, addr: usize, v: V, at: usize) -> Result<()> {
150 self.check_addr::<V>(addr, at, "store")?;
151 LittleEndian::write(&mut self.data, addr, v);
152 Ok(())
153 }
154
155 pub fn data(&self) -> &'_ [u8] {
156 &self.data
157 }
158
159 pub fn data_mut(&mut self) -> &mut [u8] {
160 &mut self.data
161 }
162}