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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
// Copyright (c) The Diem Core Contributors
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0
use std::{collections::BTreeSet, sync::Arc};
use crate::{
data_cache::TransactionDataCache, native_extensions::NativeContextExtensions,
native_functions::NativeFunction, runtime::VMRuntime, session::Session,
};
use move_binary_format::{
errors::{Location, VMResult},
CompiledModule,
};
use move_bytecode_verifier::VerifierConfig;
use move_core_types::{
account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId,
metadata::Metadata, resolver::MoveResolver,
};
pub struct MoveVM {
runtime: VMRuntime,
}
impl MoveVM {
pub fn new(
natives: impl IntoIterator<Item = (AccountAddress, Identifier, Identifier, NativeFunction)>,
) -> VMResult<Self> {
Self::new_with_verifier_config(natives, VerifierConfig::default())
}
pub fn new_with_verifier_config(
natives: impl IntoIterator<Item = (AccountAddress, Identifier, Identifier, NativeFunction)>,
verifier_config: VerifierConfig,
) -> VMResult<Self> {
Ok(Self {
runtime: VMRuntime::new(natives, verifier_config)
.map_err(|err| err.finish(Location::Undefined))?,
})
}
/// Create a new Session backed by the given storage.
///
/// Right now it is the caller's responsibility to ensure cache coherence of the Move VM Loader
/// - When a module gets published in a Move VM Session, and then gets used by another
/// transaction, it will be loaded into the code cache and stay there even if the resulted
/// effects do not get commited back to the storage when the Session ends.
/// - As a result, if one wants to have multiple sessions at a time, one needs to make sure
/// none of them will try to publish a module. In other words, if there is a module publishing
/// Session it must be the only Session existing.
/// - In general, a new Move VM needs to be created whenever the storage gets modified by an
/// outer envrionment, or otherwise the states may be out of sync. There are a few exceptional
/// cases where this may not be necessary, with the most notable one being the common module
/// publishing flow: you can keep using the same Move VM if you publish some modules in a Session
/// and apply the effects to the storage when the Session ends.
pub fn new_session<'r, S: MoveResolver>(&self, remote: &'r S) -> Session<'r, '_, S> {
self.runtime.new_session(remote)
}
/// Create a new session, as in `new_session`, but provide native context extensions.
pub fn new_session_with_extensions<'r, S: MoveResolver>(
&self,
remote: &'r S,
extensions: NativeContextExtensions<'r>,
) -> Session<'r, '_, S> {
self.runtime.new_session_with_extensions(remote, extensions)
}
/// Load a module into VM's code cache
pub fn load_module<'r, S: MoveResolver>(
&self,
module_id: &ModuleId,
remote: &'r S,
) -> VMResult<Arc<CompiledModule>> {
self.runtime
.loader()
.load_module(
module_id,
&TransactionDataCache::new(remote, self.runtime.loader()),
)
.map(|arc_module| arc_module.arc_module())
}
/// Allows the adapter to announce to the VM that the code loading cache should be considered
/// outdated. This can happen if the adapter executed a particular code publishing transaction
/// but decided to not commit the result to the data store. Because the code cache currently
/// does not support deletion, the cache will, incorrectly, still contain this module.
/// TODO: new loader architecture
pub fn mark_loader_cache_as_invalid(&self) {
self.runtime.loader().mark_as_invalid()
}
/// If the loader cache has been invalidated (either by the above call or by internal logic)
/// flush it so it is valid again. Notice that should only be called if there are no
/// outstanding sessions created from this VM.
/// TODO: new loader architecture
pub fn flush_loader_cache_if_invalidated(&self) {
self.runtime.loader().flush_if_invalidated()
}
/// Gets and clears module cache hits. This is hack which allows the adapter to see module
/// reads if executing multiple transactions in a VM. Without this, the adapter only sees
/// the first load of a module.
/// TODO: new loader architecture
pub fn get_and_clear_module_cache_hits(&self) -> BTreeSet<ModuleId> {
self.runtime.loader().get_and_clear_module_cache_hits()
}
/// Attempts to discover metadata in a given module with given key. Availability
/// of this data may depend on multiple aspects. In general, no hard assumptions of
/// availability should be made, but typically, one can expect that
/// the modules which have been involved in the execution of the last session are available.
///
/// This is called by an adapter to extract, for example, debug information out of
/// the metadata section of the code for post mortem analysis. Notice that because
/// of ownership of the underlying binary representation of modules hidden behind an rwlock,
/// this actually has to hand back a copy of the associated metadata, so metadata should
/// be organized keeping this in mind.
///
/// TODO: in the new loader architecture, as the loader is visible to the adapter, one would
/// call this directly via the loader instead of the VM.
pub fn get_module_metadata(&self, module: ModuleId, key: &[u8]) -> Option<Metadata> {
self.runtime.loader().get_metadata(module, key)
}
}