1use crate::source::describe_position;
10use std::fmt;
11use wain_ast::*;
12
13pub struct ComposeError<'source> {
14 dest_mod_id: Option<&'source str>,
15 dest_mod_offset: usize,
16 src_mod_id: Option<&'source str>,
17 src_mod_offset: usize,
18 offset: usize,
19 source: &'source str,
20 msg: String,
21}
22
23impl<'s> ComposeError<'s> {
24 pub fn source(&self) -> &'s str {
25 self.source
26 }
27 pub fn offset(&self) -> usize {
28 self.offset
29 }
30}
31
32impl<'s> fmt::Display for ComposeError<'s> {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 f.write_str("module ")?;
35 if let Some(id) = self.src_mod_id {
36 write!(f, "'{}' ", id)?;
37 }
38 write!(
39 f,
40 "at offset {} cannot be merged into existing module ",
41 self.src_mod_offset
42 )?;
43
44 if let Some(id) = self.dest_mod_id {
45 write!(f, "'{}' ", id)?;
46 }
47 write!(f, "at offset {}: {}", self.dest_mod_offset, self.msg)?;
48
49 describe_position(f, self.source, self.offset)
50 }
51}
52
53type Result<'s, T> = ::std::result::Result<T, Box<ComposeError<'s>>>;
54
55pub(crate) struct Composer<'source> {
56 source: &'source str,
57 target: Module<'source>,
58 composed_mod_id: Option<&'source str>,
59 composed_mod_offset: usize,
60 composing_imports: bool,
61}
62
63impl<'s> Composer<'s> {
64 pub(crate) fn new(target: Module<'s>, source: &'s str) -> Self {
65 Composer {
66 source,
67 target,
68 composed_mod_id: None,
69 composed_mod_offset: Default::default(),
70 composing_imports: false,
71 }
72 }
73
74 pub(crate) fn error<T>(&self, msg: String, offset: usize) -> Result<'s, T> {
75 Err(Box::new(ComposeError {
76 dest_mod_id: self.target.id,
77 dest_mod_offset: self.target.start,
78 src_mod_id: self.composed_mod_id,
79 src_mod_offset: self.composed_mod_offset,
80 offset,
81 source: self.source,
82 msg,
83 }))
84 }
85
86 pub(crate) fn compose(mut self, mut composed: Module<'s>) -> Result<Module<'s>> {
87 self.composed_mod_id = composed.id;
88 self.composed_mod_offset = composed.start;
89
90 if let (Some(s1), Some(s2)) = (&self.target.entrypoint, &composed.entrypoint) {
92 let msg = format!(
93 "start function can appear only once across modules: previous start function was defined at offset {}",
94 s1.start
95 );
96 return self.error(msg, s2.start);
97 }
98
99 composed.adjust(&mut self)?;
101
102 if !(self.target.funcs.is_empty()
104 && self.target.tables.is_empty()
105 && self.target.memories.is_empty()
106 && self.target.globals.is_empty()
107 || !self.composing_imports)
108 {
109 let msg = "when module M1 is merged into module M2, one of (1) or (2) must be met. \
110 (1) M1 has no 'import' section. \
111 (2) M2 has no 'func', 'table', 'memory' sections"
112 .to_string();
113 return self.error(msg, self.target.start);
114 }
115
116 self.target.types.append(&mut composed.types);
118 self.target.exports.append(&mut composed.exports);
119 self.target.funcs.append(&mut composed.funcs);
120 self.target.elems.append(&mut composed.elems);
121 self.target.tables.append(&mut composed.tables);
122 self.target.data.append(&mut composed.data);
123 self.target.memories.append(&mut composed.memories);
124 self.target.globals.append(&mut composed.globals);
125 if let Some(start) = composed.entrypoint {
126 self.target.entrypoint = Some(start);
127 }
128
129 Ok(self.target)
130 }
131
132 fn saw_import(&mut self, has_import: bool) {
133 self.composing_imports = self.composing_imports || has_import;
134 }
135
136 fn adjust_func_idx(&self, idx: &mut u32) {
137 *idx += self.target.funcs.len() as u32;
138 }
139
140 fn adjust_type_idx(&self, idx: &mut u32) {
141 *idx += self.target.types.len() as u32;
142 }
143
144 fn adjust_global_idx(&self, idx: &mut u32) {
145 *idx += self.target.globals.len() as u32;
146 }
147
148 fn adjust_mem_idx(&self, idx: &mut u32) {
149 *idx += self.target.memories.len() as u32;
150 }
151
152 fn adjust_table_idx(&self, idx: &mut u32) {
153 *idx += self.target.tables.len() as u32;
154 }
155}
156
157trait Adjust<'s>: Sized {
162 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()>;
163}
164
165impl<'s, A: Adjust<'s>> Adjust<'s> for Vec<A> {
166 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
167 self.iter_mut().try_for_each(|a| a.adjust(composer))
168 }
169}
170
171impl<'s, A: Adjust<'s>> Adjust<'s> for Option<A> {
172 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
173 match self {
174 Some(node) => node.adjust(composer),
175 None => Ok(()),
176 }
177 }
178}
179
180impl<'s> Adjust<'s> for Module<'s> {
181 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
182 self.entrypoint.adjust(composer)?;
183 self.exports.adjust(composer)?;
184 self.funcs.adjust(composer)?;
185 self.elems.adjust(composer)?;
186 for table in self.tables.iter() {
187 composer.saw_import(table.import.is_some());
188 }
189 self.data.adjust(composer)?;
190 self.memories.adjust(composer)?;
191 self.globals.adjust(composer)?;
192 Ok(())
193 }
194}
195
196impl<'s> Adjust<'s> for StartFunction {
197 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
198 composer.adjust_func_idx(&mut self.idx);
199 Ok(())
200 }
201}
202
203impl<'s> Adjust<'s> for Func<'s> {
204 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
205 composer.adjust_type_idx(&mut self.idx);
206 match &mut self.kind {
207 FuncKind::Import(_) => composer.saw_import(true),
208 FuncKind::Body { expr, .. } => expr.adjust(composer)?,
209 }
210 Ok(())
211 }
212}
213
214impl<'s> Adjust<'s> for Instruction {
215 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
216 use InsnKind::*;
217 match &mut self.kind {
218 Block { body, .. } => body.adjust(composer)?,
219 Loop { body, .. } => body.adjust(composer)?,
220 If {
221 then_body, else_body, ..
222 } => {
223 then_body.adjust(composer)?;
224 else_body.adjust(composer)?;
225 }
226 Call(idx) => composer.adjust_func_idx(idx),
227 CallIndirect(idx) => composer.adjust_type_idx(idx),
228 GlobalGet(idx) => composer.adjust_global_idx(idx),
229 GlobalSet(idx) => composer.adjust_global_idx(idx),
230 _ => {}
231 }
232 Ok(())
233 }
234}
235
236impl<'s> Adjust<'s> for Export<'s> {
237 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
238 match &mut self.kind {
239 ExportKind::Func(idx) => composer.adjust_func_idx(idx),
240 ExportKind::Table(idx) => composer.adjust_table_idx(idx),
241 ExportKind::Memory(idx) => composer.adjust_mem_idx(idx),
242 ExportKind::Global(idx) => composer.adjust_global_idx(idx),
243 }
244 Ok(())
245 }
246}
247
248impl<'s> Adjust<'s> for ElemSegment {
249 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
250 composer.adjust_table_idx(&mut self.idx);
251 for idx in self.init.iter_mut() {
252 composer.adjust_func_idx(idx);
253 }
254 self.offset.adjust(composer)
255 }
256}
257
258impl<'s> Adjust<'s> for DataSegment<'s> {
259 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
260 composer.adjust_mem_idx(&mut self.idx);
261 self.offset.adjust(composer)
262 }
263}
264
265impl<'s> Adjust<'s> for Memory<'s> {
266 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
267 composer.saw_import(self.import.is_some());
268 Ok(())
269 }
270}
271
272impl<'s> Adjust<'s> for Global<'s> {
273 fn adjust(&mut self, composer: &mut Composer) -> Result<'s, ()> {
274 match &mut self.kind {
275 GlobalKind::Import(_) => composer.saw_import(true),
276 GlobalKind::Init(init) => init.adjust(composer)?,
277 }
278 Ok(())
279 }
280}