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}