tetsy_wasm/builder/
module.rs

1use alloc::vec::Vec;
2use crate::elements;
3use super::{
4	import,
5	export,
6	global,
7	data,
8	invoke::{Invoke, Identity},
9	code::{self, SignaturesBuilder, FunctionBuilder},
10	memory::{self, MemoryBuilder},
11	table::{self, TableBuilder},
12};
13
14/// Module builder
15pub struct ModuleBuilder<F=Identity> {
16	callback: F,
17	module: ModuleScaffold,
18}
19
20/// Location of the internal module function
21pub struct CodeLocation {
22	/// Location (index in 'functions' section) of the signature
23	pub signature: u32,
24	/// Location (index in the 'code' section) of the body
25	pub body: u32,
26}
27
28#[derive(Default, PartialEq)]
29struct ModuleScaffold {
30	pub types: elements::TypeSection,
31	pub import: elements::ImportSection,
32	pub functions: elements::FunctionSection,
33	pub table: elements::TableSection,
34	pub memory: elements::MemorySection,
35	pub global: elements::GlobalSection,
36	pub export: elements::ExportSection,
37	pub start: Option<u32>,
38	pub element: elements::ElementSection,
39	pub code: elements::CodeSection,
40	pub data: elements::DataSection,
41	pub other: Vec<elements::Section>,
42}
43
44impl From<elements::Module> for ModuleScaffold {
45	fn from(module: elements::Module) -> Self {
46		let mut types: Option<elements::TypeSection> = None;
47		let mut import: Option<elements::ImportSection> = None;
48		let mut funcs: Option<elements::FunctionSection> = None;
49		let mut table: Option<elements::TableSection> = None;
50		let mut memory: Option<elements::MemorySection> = None;
51		let mut global: Option<elements::GlobalSection> = None;
52		let mut export: Option<elements::ExportSection> = None;
53		let mut start: Option<u32> = None;
54		let mut element: Option<elements::ElementSection> = None;
55		let mut code: Option<elements::CodeSection> = None;
56		let mut data: Option<elements::DataSection> = None;
57
58		let mut other = Vec::new();
59		let mut sections = module.into_sections();
60		while let Some(section) = sections.pop() {
61			match section {
62				elements::Section::Type(sect) => { types = Some(sect); }
63				elements::Section::Import(sect) => { import = Some(sect); }
64				elements::Section::Function(sect) => { funcs = Some(sect); }
65				elements::Section::Table(sect) => { table = Some(sect); }
66				elements::Section::Memory(sect) => { memory = Some(sect); }
67				elements::Section::Global(sect) => { global = Some(sect); }
68				elements::Section::Export(sect) => { export = Some(sect); }
69				elements::Section::Start(index) => { start = Some(index); }
70				elements::Section::Element(sect) => { element = Some(sect); }
71				elements::Section::Code(sect) => { code = Some(sect); }
72				elements::Section::Data(sect) => { data = Some(sect); }
73				section => other.push(section)
74			}
75		}
76
77		ModuleScaffold {
78			types: types.unwrap_or_default(),
79			import: import.unwrap_or_default(),
80			functions: funcs.unwrap_or_default(),
81			table: table.unwrap_or_default(),
82			memory: memory.unwrap_or_default(),
83			global: global.unwrap_or_default(),
84			export: export.unwrap_or_default(),
85			start: start,
86			element: element.unwrap_or_default(),
87			code: code.unwrap_or_default(),
88			data: data.unwrap_or_default(),
89			other,
90		}
91	}
92}
93
94impl From<ModuleScaffold> for elements::Module {
95	fn from(module: ModuleScaffold) -> Self {
96		let mut sections = Vec::new();
97
98		let types = module.types;
99		if types.types().len() > 0 {
100			sections.push(elements::Section::Type(types));
101		}
102		let import = module.import;
103		if import.entries().len() > 0 {
104			sections.push(elements::Section::Import(import));
105		}
106		let functions = module.functions;
107		if functions.entries().len() > 0 {
108			sections.push(elements::Section::Function(functions));
109		}
110		let table = module.table;
111		if table.entries().len() > 0 {
112			sections.push(elements::Section::Table(table));
113		}
114		let memory = module.memory;
115		if memory.entries().len() > 0 {
116			sections.push(elements::Section::Memory(memory));
117		}
118		let global = module.global;
119		if global.entries().len() > 0 {
120			sections.push(elements::Section::Global(global));
121		}
122		let export = module.export;
123		if export.entries().len() > 0 {
124			sections.push(elements::Section::Export(export));
125		}
126		if let Some(start) = module.start {
127			sections.push(elements::Section::Start(start));
128		}
129		let element = module.element;
130		if element.entries().len() > 0 {
131			sections.push(elements::Section::Element(element));
132		}
133		let code = module.code;
134		if code.bodies().len() > 0 {
135			sections.push(elements::Section::Code(code));
136		}
137		let data = module.data;
138		if data.entries().len() > 0 {
139			sections.push(elements::Section::Data(data));
140		}
141		sections.extend(module.other);
142		elements::Module::new(sections)
143	}
144}
145
146impl ModuleBuilder {
147	/// New empty module builder
148	pub fn new() -> Self {
149		ModuleBuilder::with_callback(Identity)
150	}
151}
152
153impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
154	/// New module builder with bound callback
155	pub fn with_callback(callback: F) -> Self {
156		ModuleBuilder {
157			callback: callback,
158			module: Default::default(),
159		}
160	}
161
162	/// Builder from raw module
163	pub fn with_module(mut self, module: elements::Module) -> Self {
164		self.module = module.into();
165		self
166	}
167
168	/// Fill module with sections from iterator
169	pub fn with_sections<I>(mut self, sections: I) -> Self
170		where I: IntoIterator<Item=elements::Section>
171	{
172		self.module.other.extend(sections);
173		self
174	}
175
176	/// Add additional section
177	pub fn with_section(mut self, section: elements::Section) -> Self {
178		self.module.other.push(section);
179		self
180	}
181
182	/// Binds to the type section, creates additional types when required
183	pub fn with_signatures(mut self, bindings: code::SignatureBindings) -> Self {
184		self.push_signatures(bindings);
185		self
186	}
187
188	/// Push stand-alone function definition, creating sections, signature and code blocks
189	/// in corresponding sections.
190	/// `FunctionDefinition` can be build using `builder::function` builder
191	pub fn push_function(&mut self, func: code::FunctionDefinition) -> CodeLocation {
192		let signature = func.signature;
193		let body = func.code;
194
195		let type_ref = self.resolve_type_ref(signature);
196
197		self.module.functions.entries_mut().push(elements::Func::new(type_ref));
198		let signature_index = self.module.functions.entries_mut().len() as u32 - 1;
199		self.module.code.bodies_mut().push(body);
200		let body_index = self.module.code.bodies_mut().len() as u32 - 1;
201
202		if func.is_main {
203			self.module.start = Some(body_index);
204		}
205
206		CodeLocation {
207			signature: signature_index,
208			body: body_index,
209		}
210	}
211
212	/// Push linear memory region
213	pub fn push_memory(&mut self, mut memory: memory::MemoryDefinition) -> u32 {
214		let entries = self.module.memory.entries_mut();
215		entries.push(elements::MemoryType::new(memory.min, memory.max));
216		let memory_index = (entries.len() - 1) as u32;
217		for data in memory.data.drain(..) {
218			self.module.data.entries_mut()
219				.push(elements::DataSegment::new(memory_index, Some(data.offset), data.values))
220		}
221		memory_index
222	}
223
224	/// Push table
225	pub fn push_table(&mut self, mut table: table::TableDefinition) -> u32 {
226		let entries = self.module.table.entries_mut();
227		entries.push(elements::TableType::new(table.min, table.max));
228		let table_index = (entries.len() - 1) as u32;
229		for entry in table.elements.drain(..) {
230			self.module.element.entries_mut()
231				.push(elements::ElementSegment::new(table_index, Some(entry.offset), entry.values))
232		}
233		table_index
234	}
235
236	/// Push global.
237	pub fn push_global(&mut self, global: elements::GlobalEntry) -> u32 {
238		let entries = self.module.global.entries_mut();
239		entries.push(global);
240		entries.len() as u32 - 1
241	}
242
243	fn resolve_type_ref(&mut self, signature: code::Signature) -> u32 {
244		match signature {
245			code::Signature::Inline(func_type) => {
246				if let Some(existing_entry) = self.module.types.types().iter().enumerate().find(|(_idx, t)| {
247					let elements::Type::Function(ref existing) = t;
248					*existing == func_type
249				}) {
250					return existing_entry.0 as u32
251				}
252				self.module.types.types_mut().push(elements::Type::Function(func_type));
253				self.module.types.types().len() as u32 - 1
254			}
255			code::Signature::TypeReference(type_ref) => {
256				type_ref
257			}
258		}
259	}
260
261	/// Push one function signature, returning it's calling index.
262	/// Can create corresponding type in type section.
263	pub fn push_signature(&mut self, signature: code::Signature) -> u32 {
264		self.resolve_type_ref(signature)
265	}
266
267	/// Push signatures in the module, returning corresponding indices of pushed signatures
268	pub fn push_signatures(&mut self, signatures: code::SignatureBindings) -> Vec<u32> {
269		signatures.into_iter().map(|binding|
270			self.resolve_type_ref(binding)
271		).collect()
272	}
273
274	/// Push import entry to module. Note that this does not update calling indices in
275	/// function bodies.
276	pub fn push_import(&mut self, import: elements::ImportEntry) -> u32 {
277		self.module.import.entries_mut().push(import);
278		// todo: actually update calling addresses in function bodies
279		// todo: also batch push
280
281		self.module.import.entries_mut().len() as u32 - 1
282	}
283
284	/// Push export entry to module.
285	pub fn push_export(&mut self, export: elements::ExportEntry) -> u32 {
286		self.module.export.entries_mut().push(export);
287		self.module.export.entries_mut().len() as u32 - 1
288	}
289
290	/// Add new function using dedicated builder
291	pub fn function(self) -> FunctionBuilder<Self> {
292		FunctionBuilder::with_callback(self)
293	}
294
295	/// Add new linear memory using dedicated builder
296	pub fn memory(self) -> MemoryBuilder<Self> {
297		MemoryBuilder::with_callback(self)
298	}
299
300	/// Add new table using dedicated builder
301	pub fn table(self) -> TableBuilder<Self> {
302		TableBuilder::with_callback(self)
303	}
304
305	/// Define functions section
306	pub fn functions(self) -> SignaturesBuilder<Self> {
307		SignaturesBuilder::with_callback(self)
308	}
309
310	/// With inserted export entry
311	pub fn with_export(mut self, entry: elements::ExportEntry) -> Self {
312		self.module.export.entries_mut().push(entry);
313		self
314	}
315
316	/// With inserted import entry
317	pub fn with_import(mut self, entry: elements::ImportEntry) -> Self {
318		self.module.import.entries_mut().push(entry);
319		self
320	}
321
322	/// Import entry builder
323	/// # Examples
324	/// ```
325	/// use tetsy_wasm::builder::module;
326	///
327	/// let module = module()
328	///    .import()
329	///        .module("env")
330	///        .field("memory")
331	///        .external().memory(256, Some(256))
332	///        .build()
333	///    .build();
334	///
335	/// assert_eq!(module.import_section().expect("import section to exist").entries().len(), 1);
336	/// ```
337	pub fn import(self) -> import::ImportBuilder<Self> {
338		import::ImportBuilder::with_callback(self)
339	}
340
341	/// With global variable
342	pub fn with_global(mut self, global: elements::GlobalEntry) -> Self {
343		self.module.global.entries_mut().push(global);
344		self
345	}
346
347	/// With table
348	pub fn with_table(mut self, table: elements::TableType) -> Self {
349		self.module.table.entries_mut().push(table);
350		self
351	}
352
353	/// Export entry builder
354	/// # Examples
355	/// ```
356	/// use tetsy_wasm::builder::module;
357	/// use tetsy_wasm::elements::Instruction::*;
358	///
359	/// let module = module()
360	///    .global()
361	///         .value_type().i32()
362	///         .init_expr(I32Const(0))
363	///         .build()
364	///    .export()
365	///        .field("_zero")
366	///        .internal().global(0)
367	///        .build()
368	///    .build();
369	///
370	/// assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1);
371	/// ```
372	pub fn export(self) -> export::ExportBuilder<Self> {
373		export::ExportBuilder::with_callback(self)
374	}
375
376	/// Glboal entry builder
377	/// # Examples
378	/// ```
379	/// use tetsy_wasm::builder::module;
380	/// use tetsy_wasm::elements::Instruction::*;
381	///
382	/// let module = module()
383	///    .global()
384	///         .value_type().i32()
385	///         .init_expr(I32Const(0))
386	///         .build()
387	///    .build();
388	///
389	/// assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1);
390	/// ```
391	pub fn global(self) -> global::GlobalBuilder<Self> {
392		global::GlobalBuilder::with_callback(self)
393	}
394
395	/// Add data segment to the builder
396	pub fn with_data_segment(mut self, segment: elements::DataSegment) -> Self {
397		self.module.data.entries_mut().push(segment);
398		self
399	}
400
401	/// Data entry builder
402	pub fn data(self) -> data::DataSegmentBuilder<Self> {
403		data::DataSegmentBuilder::with_callback(self)
404	}
405
406	/// Build module (final step)
407	pub fn build(self) -> F::Result {
408		self.callback.invoke(self.module.into())
409	}
410}
411
412impl<F> Invoke<elements::FunctionSection> for ModuleBuilder<F>
413	where F: Invoke<elements::Module>
414{
415	type Result = Self;
416
417	fn invoke(self, section: elements::FunctionSection) -> Self {
418		self.with_section(elements::Section::Function(section))
419	}
420}
421
422impl<F> Invoke<code::SignatureBindings> for ModuleBuilder<F>
423	where F: Invoke<elements::Module>
424{
425	type Result = Self;
426
427	fn invoke(self, bindings: code::SignatureBindings) -> Self {
428		self.with_signatures(bindings)
429	}
430}
431
432
433impl<F> Invoke<code::FunctionDefinition> for ModuleBuilder<F>
434	where F: Invoke<elements::Module>
435{
436	type Result = Self;
437
438	fn invoke(self, def: code::FunctionDefinition) -> Self {
439		let mut b = self;
440		b.push_function(def);
441		b
442	}
443}
444
445impl<F> Invoke<memory::MemoryDefinition> for ModuleBuilder<F>
446	where F: Invoke<elements::Module>
447{
448	type Result = Self;
449
450	fn invoke(self, def: memory::MemoryDefinition) -> Self {
451		let mut b = self;
452		b.push_memory(def);
453		b
454	}
455}
456
457impl<F> Invoke<table::TableDefinition> for ModuleBuilder<F>
458	where F: Invoke<elements::Module>
459{
460	type Result = Self;
461
462	fn invoke(self, def: table::TableDefinition) -> Self {
463		let mut b = self;
464		b.push_table(def);
465		b
466	}
467}
468
469impl<F> Invoke<elements::ImportEntry> for ModuleBuilder<F>
470	where F: Invoke<elements::Module>
471{
472	type Result = Self;
473
474	fn invoke(self, entry: elements::ImportEntry) -> Self::Result {
475		self.with_import(entry)
476	}
477}
478
479impl<F> Invoke<elements::ExportEntry> for ModuleBuilder<F>
480	where F: Invoke<elements::Module>
481{
482	type Result = Self;
483
484	fn invoke(self, entry: elements::ExportEntry) -> Self::Result {
485		self.with_export(entry)
486	}
487}
488
489impl<F> Invoke<elements::GlobalEntry> for ModuleBuilder<F>
490	where F: Invoke<elements::Module>
491{
492	type Result = Self;
493
494	fn invoke(self, entry: elements::GlobalEntry) -> Self::Result {
495		self.with_global(entry)
496	}
497}
498
499impl<F> Invoke<elements::DataSegment> for ModuleBuilder<F>
500	where F: Invoke<elements::Module>
501{
502	type Result = Self;
503
504	fn invoke(self, segment: elements::DataSegment) -> Self {
505		self.with_data_segment(segment)
506	}
507}
508
509/// Start new module builder
510/// # Examples
511///
512/// ```
513/// use tetsy_wasm::builder;
514///
515/// let module = builder::module()
516///     .function()
517///         .signature().param().i32().build()
518///         .body().build()
519///         .build()
520///     .build();
521///
522/// assert_eq!(module.type_section().expect("type section to exist").types().len(), 1);
523/// assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1);
524/// assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1);
525/// ```
526pub fn module() -> ModuleBuilder {
527	ModuleBuilder::new()
528}
529
530/// Start builder to extend existing module
531pub fn from_module(module: elements::Module) -> ModuleBuilder {
532	ModuleBuilder::new().with_module(module)
533}
534
535#[cfg(test)]
536mod tests {
537
538	use crate::elements;
539	use super::module;
540
541	#[test]
542	fn smoky() {
543		let module = module().build();
544		assert_eq!(module.sections().len(), 0);
545	}
546
547	#[test]
548	fn functions() {
549		let module = module()
550			.function()
551				.signature().param().i32().build()
552				.body().build()
553				.build()
554			.build();
555
556		assert_eq!(module.type_section().expect("type section to exist").types().len(), 1);
557		assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1);
558		assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1);
559	}
560
561	#[test]
562	fn export() {
563		let module = module()
564			.export().field("call").internal().func(0).build()
565			.build();
566
567		assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1);
568	}
569
570	#[test]
571	fn global() {
572		let module = module()
573			.global().value_type().i64().mutable().init_expr(elements::Instruction::I64Const(5)).build()
574			.build();
575
576		assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1);
577	}
578
579	#[test]
580	fn data() {
581		let module = module()
582			.data()
583				.offset(elements::Instruction::I32Const(16))
584				.value(vec![0u8, 15, 10, 5, 25])
585				.build()
586			.build();
587
588		assert_eq!(module.data_section().expect("data section to exist").entries().len(), 1);
589	}
590
591	#[test]
592	fn reuse_types() {
593		let module = module()
594			.function()
595				.signature().param().i32().build()
596				.body().build()
597				.build()
598			.function()
599				.signature().param().i32().build()
600				.body().build()
601				.build()
602			.build();
603
604		assert_eq!(module.type_section().expect("type section failed").types().len(), 1);
605	}
606 }