tc_executor_common/
util.rs

1// This file is part of Tetcore.
2
3// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! A set of utilities for resetting a wasm instance to its initial state.
20
21use crate::error::{self, Error};
22use std::mem;
23use tetsy_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule};
24
25/// A bunch of information collected from a WebAssembly module.
26pub struct WasmModuleInfo {
27	raw_module: RawModule,
28}
29
30impl WasmModuleInfo {
31	/// Create `WasmModuleInfo` from the given wasm code.
32	///
33	/// Returns `None` if the wasm code cannot be deserialized.
34	pub fn new(wasm_code: &[u8]) -> Option<Self> {
35		let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?;
36		Some(Self { raw_module })
37	}
38
39	/// Extract the data segments from the given wasm code.
40	///
41	/// Returns `Err` if the given wasm code cannot be deserialized.
42	fn data_segments(&self) -> Vec<DataSegment> {
43		self.raw_module
44			.data_section()
45			.map(|ds| ds.entries())
46			.unwrap_or(&[])
47			.to_vec()
48	}
49
50	/// The number of globals defined in locally in this module.
51	pub fn declared_globals_count(&self) -> u32 {
52		self.raw_module
53			.global_section()
54			.map(|gs| gs.entries().len() as u32)
55			.unwrap_or(0)
56	}
57
58	/// The number of imports of globals.
59	pub fn imported_globals_count(&self) -> u32 {
60		self.raw_module
61			.import_section()
62			.map(|is| is.globals() as u32)
63			.unwrap_or(0)
64	}
65}
66
67/// This is a snapshot of data segments specialzied for a particular instantiation.
68///
69/// Note that this assumes that no mutable globals are used.
70#[derive(Clone)]
71pub struct DataSegmentsSnapshot {
72	/// The list of data segments represented by (offset, contents).
73	data_segments: Vec<(u32, Vec<u8>)>,
74}
75
76impl DataSegmentsSnapshot {
77	/// Create a snapshot from the data segments from the module.
78	pub fn take(module: &WasmModuleInfo) -> error::Result<Self> {
79		let data_segments = module
80			.data_segments()
81			.into_iter()
82			.map(|mut segment| {
83				// Just replace contents of the segment since the segments will be discarded later
84				// anyway.
85				let contents = mem::replace(segment.value_mut(), vec![]);
86
87				let init_expr = match segment.offset() {
88					Some(offset) => offset.code(),
89					// Return if the segment is passive
90					None => return Err(Error::SharedMemUnsupported),
91				};
92
93				// [op, End]
94				if init_expr.len() != 2 {
95					return Err(Error::InitializerHasTooManyExpressions);
96				}
97				let offset = match &init_expr[0] {
98					Instruction::I32Const(v) => *v as u32,
99					Instruction::GetGlobal(_) => {
100						// In a valid wasm file, initializer expressions can only refer imported
101						// globals.
102						//
103						// At the moment of writing the Tetcore Runtime Interface does not provide
104						// any globals. There is nothing that prevents us from supporting this
105						// if/when we gain those.
106						return Err(Error::ImportedGlobalsUnsupported);
107					}
108					insn => {
109						return Err(Error::InvalidInitializerExpression(format!("{:?}", insn)))
110					}
111				};
112
113				Ok((offset, contents))
114			})
115			.collect::<error::Result<Vec<_>>>()?;
116
117		Ok(Self { data_segments })
118	}
119
120	/// Apply the given snapshot to a linear memory.
121	///
122	/// Linear memory interface is represented by a closure `memory_set`.
123	pub fn apply<E>(
124		&self,
125		mut memory_set: impl FnMut(u32, &[u8]) -> Result<(), E>,
126	) -> Result<(), E> {
127		for (offset, contents) in &self.data_segments {
128			memory_set(*offset, contents)?;
129		}
130		Ok(())
131	}
132}