1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
// This file is part of Substrate.

// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use super::RuntimeBlob;

/// Saved value of particular exported global.
struct SavedValue<Global> {
	/// The handle of this global which can be used to refer to this global.
	handle: Global,
	/// The global value that was observed during the snapshot creation.
	value: sp_wasm_interface::Value,
}

/// An adapter for a wasm module instance that is focused on getting and setting globals.
pub trait InstanceGlobals {
	/// A handle to a global which can be used to get or set a global variable. This is supposed to
	/// be a lightweight handle, like an index or an Rc-like smart-pointer, which is cheap to clone.
	type Global: Clone;
	/// Get a handle to a global by it's export name.
	///
	/// The requested export is must exist in the exported list, and it should be a mutable global.
	fn get_global(&self, export_name: &str) -> Self::Global;
	/// Get the current value of the global.
	fn get_global_value(&self, global: &Self::Global) -> sp_wasm_interface::Value;
	/// Update the current value of the global.
	///
	/// The global behind the handle is guaranteed to be mutable and the value to be the same type
	/// as the global.
	fn set_global_value(&self, global: &Self::Global, value: sp_wasm_interface::Value);
}

/// A set of exposed mutable globals.
///
/// This is set of globals required to create a [`GlobalsSnapshot`] and that are collected from
/// a runtime blob that was instrumented by
/// [`RuntimeBlob::expose_mutable_globals`](super::RuntimeBlob::expose_mutable_globals`).

/// If the code wasn't instrumented then it would be empty and snapshot would do nothing.
pub struct ExposedMutableGlobalsSet(Vec<String>);

impl ExposedMutableGlobalsSet {
	/// Collect the set from the given runtime blob. See the struct documentation for details.
	pub fn collect(runtime_blob: &RuntimeBlob) -> Self {
		let global_names =
			runtime_blob.exported_internal_global_names().map(ToOwned::to_owned).collect();
		Self(global_names)
	}
}

/// A snapshot of a global variables values. This snapshot can be later used for restoring the
/// values to the preserved state.
///
/// Technically, a snapshot stores only values of mutable global variables. This is because
/// immutable global variables always have the same values.
///
/// We take it from an instance rather from a module because the start function could potentially
/// change any of the mutable global values.
pub struct GlobalsSnapshot<Global>(Vec<SavedValue<Global>>);

impl<Global> GlobalsSnapshot<Global> {
	/// Take a snapshot of global variables for a given instance.
	///
	/// # Panics
	///
	/// This function panics if the instance doesn't correspond to the module from which the
	/// [`ExposedMutableGlobalsSet`] was collected.
	pub fn take<Instance>(mutable_globals: &ExposedMutableGlobalsSet, instance: &Instance) -> Self
	where
		Instance: InstanceGlobals<Global = Global>,
	{
		let global_names = &mutable_globals.0;
		let mut saved_values = Vec::with_capacity(global_names.len());

		for global_name in global_names {
			let handle = instance.get_global(global_name);
			let value = instance.get_global_value(&handle);
			saved_values.push(SavedValue { handle, value });
		}

		Self(saved_values)
	}

	/// Apply the snapshot to the given instance.
	///
	/// This instance must be the same that was used for creation of this snapshot.
	pub fn apply<Instance>(&self, instance: &Instance)
	where
		Instance: InstanceGlobals<Global = Global>,
	{
		for saved_value in &self.0 {
			instance.set_global_value(&saved_value.handle, saved_value.value);
		}
	}
}