use anyhow::{bail, format_err, Result};
use move_binary_format::{
access::ModuleAccess,
file_format::{
AbilitySet, AddressIdentifierIndex, CodeOffset, Constant, ConstantPoolIndex, FieldHandle,
FieldHandleIndex, FieldInstantiation, FieldInstantiationIndex, FunctionDefinitionIndex,
FunctionHandle, FunctionHandleIndex, FunctionInstantiation, FunctionInstantiationIndex,
FunctionSignature, IdentifierIndex, ModuleHandle, ModuleHandleIndex, Signature,
SignatureIndex, SignatureToken, StructDefInstantiation, StructDefInstantiationIndex,
StructDefinitionIndex, StructHandle, StructHandleIndex, StructTypeParameter, TableIndex,
},
CompiledModule,
};
use move_bytecode_source_map::source_map::SourceMap;
use move_core_types::{
account_address::AccountAddress,
identifier::{IdentStr, Identifier},
};
use move_ir_types::{
ast::{
BlockLabel_, ConstantName, Field_, FunctionName, ModuleIdent, ModuleName,
QualifiedStructIdent, StructName,
},
location::Loc,
};
use std::{clone::Clone, collections::HashMap, hash::Hash};
macro_rules! get_or_add_item_macro {
($m:ident, $k_get:expr, $k_insert:expr) => {{
let k_key = $k_get;
Ok(if $m.contains_key(k_key) {
*$m.get(k_key).unwrap()
} else {
let len = $m.len();
if len >= TABLE_MAX_SIZE {
bail!("Max table size reached!")
}
let index = len as TableIndex;
$m.insert($k_insert, index);
index
})
}};
}
pub const TABLE_MAX_SIZE: usize = u16::max_value() as usize;
fn get_or_add_item_ref<K: Clone + Eq + Hash>(
m: &mut HashMap<K, TableIndex>,
k: &K,
) -> Result<TableIndex> {
get_or_add_item_macro!(m, k, k.clone())
}
fn get_or_add_item<K: Eq + Hash>(m: &mut HashMap<K, TableIndex>, k: K) -> Result<TableIndex> {
get_or_add_item_macro!(m, &k, k)
}
pub fn ident_str(s: &str) -> Result<&IdentStr> {
IdentStr::new(s)
}
#[derive(Clone, Debug)]
pub struct CompiledDependencyView<'a> {
structs: HashMap<(&'a IdentStr, &'a IdentStr), TableIndex>,
functions: HashMap<&'a IdentStr, TableIndex>,
module_pool: &'a [ModuleHandle],
struct_pool: &'a [StructHandle],
function_pool: &'a [FunctionHandle],
identifiers: &'a [Identifier],
address_identifiers: &'a [AccountAddress],
signature_pool: &'a [Signature],
}
impl<'a> CompiledDependencyView<'a> {
pub fn new(dep: &'a CompiledModule) -> Result<Self> {
let mut structs = HashMap::new();
let mut functions = HashMap::new();
let self_handle = dep.self_handle_idx();
for shandle in dep.struct_handles() {
let mhandle = dep.module_handle_at(shandle.module);
let mname = dep.identifier_at(mhandle.name);
let sname = dep.identifier_at(shandle.name);
get_or_add_item(&mut structs, (mname, sname))?;
}
let defined_function_handles = dep
.function_handles()
.iter()
.enumerate()
.filter(|(_idx, fhandle)| fhandle.module == self_handle);
for (idx, fhandle) in defined_function_handles {
let fname = dep.identifier_at(fhandle.name);
functions.insert(fname, idx as u16);
}
Ok(Self {
structs,
functions,
module_pool: dep.module_handles(),
struct_pool: dep.struct_handles(),
function_pool: dep.function_handles(),
identifiers: dep.identifiers(),
address_identifiers: dep.address_identifiers(),
signature_pool: dep.signatures(),
})
}
fn source_struct_info(&self, idx: StructHandleIndex) -> Option<(ModuleIdent, StructName)> {
let handle = self.struct_pool.get(idx.0 as usize)?;
let module_handle = self.module_pool.get(handle.module.0 as usize)?;
let address = *self
.address_identifiers
.get(module_handle.address.0 as usize)?;
let module = ModuleName(
self.identifiers
.get(module_handle.name.0 as usize)?
.as_str()
.into(),
);
assert!(module != ModuleName::module_self());
let ident = ModuleIdent {
address,
name: module,
};
let name = StructName(
self.identifiers
.get(handle.name.0 as usize)?
.as_str()
.into(),
);
Some((ident, name))
}
fn struct_handle(&self, module: &ModuleName, name: &StructName) -> Option<&'a StructHandle> {
self.structs
.get(&(
ident_str(module.0.as_str()).ok()?,
ident_str(name.0.as_str()).ok()?,
))
.and_then(|idx| self.struct_pool.get(*idx as usize))
}
fn function_signature(&self, name: &FunctionName) -> Option<FunctionSignature> {
self.functions
.get(ident_str(name.0.as_str()).ok()?)
.and_then(|idx| {
let fh = self.function_pool.get(*idx as usize)?;
Some(FunctionSignature {
parameters: self.signature_pool[fh.parameters.0 as usize].0.clone(),
return_: self.signature_pool[fh.return_.0 as usize].0.clone(),
type_parameters: fh.type_parameters.clone(),
})
})
}
}
#[ouroboros::self_referencing]
pub(crate) struct StoredCompiledDependency {
module: Box<CompiledModule>,
#[borrows(module)]
#[covariant]
view: CompiledDependencyView<'this>,
}
impl StoredCompiledDependency {
pub fn create(module: CompiledModule) -> Result<Self> {
Self::try_new(Box::new(module), move |module| {
CompiledDependencyView::new(module)
})
}
}
pub(crate) enum CompiledDependency<'a> {
Borrowed(CompiledDependencyView<'a>),
Stored(StoredCompiledDependency),
}
impl<'a> CompiledDependency<'a> {
pub fn borrowed(module: &'a CompiledModule) -> Result<Self> {
Ok(Self::Borrowed(CompiledDependencyView::new(module)?))
}
pub fn stored(module: CompiledModule) -> Result<Self> {
Ok(Self::Stored(StoredCompiledDependency::create(module)?))
}
}
pub(crate) type CompiledDependencies<'a> = HashMap<ModuleIdent, CompiledDependency<'a>>;
pub struct MaterializedPools {
pub module_handles: Vec<ModuleHandle>,
pub struct_handles: Vec<StructHandle>,
pub function_handles: Vec<FunctionHandle>,
pub field_handles: Vec<FieldHandle>,
pub struct_def_instantiations: Vec<StructDefInstantiation>,
pub function_instantiations: Vec<FunctionInstantiation>,
pub field_instantiations: Vec<FieldInstantiation>,
pub signatures: Vec<Signature>,
pub identifiers: Vec<Identifier>,
pub address_identifiers: Vec<AccountAddress>,
pub constant_pool: Vec<Constant>,
}
pub(crate) struct Context<'a> {
dependencies: CompiledDependencies<'a>,
aliases: HashMap<ModuleIdent, ModuleName>,
modules: HashMap<ModuleName, (ModuleIdent, ModuleHandle)>,
structs: HashMap<QualifiedStructIdent, StructHandle>,
struct_defs: HashMap<StructName, TableIndex>,
named_constants: HashMap<ConstantName, TableIndex>,
labels: HashMap<BlockLabel_, u16>,
fields: HashMap<(StructHandleIndex, Field_), (StructDefinitionIndex, SignatureToken, usize)>,
function_handles: HashMap<(ModuleName, FunctionName), (FunctionHandle, FunctionHandleIndex)>,
function_signatures: HashMap<(ModuleName, FunctionName), FunctionSignature>,
module_handles: HashMap<ModuleHandle, TableIndex>,
struct_handles: HashMap<StructHandle, TableIndex>,
signatures: HashMap<Signature, TableIndex>,
identifiers: HashMap<Identifier, TableIndex>,
address_identifiers: HashMap<AccountAddress, TableIndex>,
constant_pool: HashMap<Constant, TableIndex>,
field_handles: HashMap<FieldHandle, TableIndex>,
struct_instantiations: HashMap<StructDefInstantiation, TableIndex>,
function_instantiations: HashMap<FunctionInstantiation, TableIndex>,
field_instantiations: HashMap<FieldInstantiation, TableIndex>,
current_function_index: FunctionDefinitionIndex,
pub source_map: SourceMap,
}
impl<'a> Context<'a> {
pub fn new(
decl_location: Loc,
dependencies: CompiledDependencies<'a>,
current_module_opt: Option<ModuleIdent>,
) -> Result<Self> {
let context = Self {
dependencies,
aliases: HashMap::new(),
modules: HashMap::new(),
structs: HashMap::new(),
struct_defs: HashMap::new(),
named_constants: HashMap::new(),
labels: HashMap::new(),
fields: HashMap::new(),
function_handles: HashMap::new(),
function_signatures: HashMap::new(),
module_handles: HashMap::new(),
struct_handles: HashMap::new(),
field_handles: HashMap::new(),
struct_instantiations: HashMap::new(),
function_instantiations: HashMap::new(),
field_instantiations: HashMap::new(),
signatures: HashMap::new(),
identifiers: HashMap::new(),
address_identifiers: HashMap::new(),
constant_pool: HashMap::new(),
current_function_index: FunctionDefinitionIndex::new(0),
source_map: SourceMap::new(decl_location, current_module_opt),
};
Ok(context)
}
pub fn take_dependencies(&mut self) -> CompiledDependencies<'a> {
std::mem::take(&mut self.dependencies)
}
pub fn restore_dependencies(&mut self, dependencies: CompiledDependencies<'a>) {
assert!(self.dependencies.is_empty());
self.dependencies = dependencies;
}
pub fn add_compiled_dependency(&mut self, compiled_dep: &'a CompiledModule) -> Result<()> {
let ident = ModuleIdent {
address: *compiled_dep.address(),
name: ModuleName(compiled_dep.name().as_str().into()),
};
match self.dependencies.get(&ident) {
None => self
.dependencies
.insert(ident, CompiledDependency::borrowed(compiled_dep)?),
Some(_previous) => bail!("Duplicate dependency module for {}", ident),
};
Ok(())
}
fn materialize_pool<T: Clone>(
size: usize,
items: impl IntoIterator<Item = (T, TableIndex)>,
) -> Vec<T> {
let mut options = vec![None; size];
for (item, idx) in items {
assert!(options[idx as usize].is_none());
options[idx as usize] = Some(item);
}
options.into_iter().map(|opt| opt.unwrap()).collect()
}
fn materialize_map<T: Clone>(m: HashMap<T, TableIndex>) -> Vec<T> {
Self::materialize_pool(m.len(), m.into_iter())
}
pub fn materialize_pools(self) -> (MaterializedPools, CompiledDependencies<'a>, SourceMap) {
let num_functions = self.function_handles.len();
assert!(num_functions == self.function_signatures.len());
let function_handles = Self::materialize_pool(
num_functions,
self.function_handles
.into_iter()
.map(|(_, (t, idx))| (t, idx.0)),
);
let materialized_pools = MaterializedPools {
function_handles,
module_handles: Self::materialize_map(self.module_handles),
struct_handles: Self::materialize_map(self.struct_handles),
field_handles: Self::materialize_map(self.field_handles),
signatures: Self::materialize_map(self.signatures),
identifiers: Self::materialize_map(self.identifiers),
address_identifiers: Self::materialize_map(self.address_identifiers),
constant_pool: Self::materialize_map(self.constant_pool),
function_instantiations: Self::materialize_map(self.function_instantiations),
struct_def_instantiations: Self::materialize_map(self.struct_instantiations),
field_instantiations: Self::materialize_map(self.field_instantiations),
};
(materialized_pools, self.dependencies, self.source_map)
}
pub fn build_index_remapping(
&mut self,
label_to_index: HashMap<BlockLabel_, u16>,
) -> HashMap<u16, u16> {
let labels = std::mem::take(&mut self.labels);
label_to_index
.into_iter()
.map(|(lbl, actual_idx)| (labels[&lbl], actual_idx))
.collect()
}
fn module_alias(&self, ident: &ModuleIdent) -> Result<&ModuleName> {
self.aliases
.get(ident)
.ok_or_else(|| format_err!("Missing import for module {}", ident))
}
fn module_handle(&self, module_name: &ModuleName) -> Result<&ModuleHandle> {
match self.modules.get(module_name) {
None => bail!("Unbound module alias {}", module_name),
Some((_, mh)) => Ok(mh),
}
}
pub fn module_ident(&self, module_name: &ModuleName) -> Result<&ModuleIdent> {
match self.modules.get(module_name) {
None => bail!("Unbound module alias {}", module_name),
Some((id, _)) => Ok(id),
}
}
pub fn module_handle_index(&self, module_name: &ModuleName) -> Result<ModuleHandleIndex> {
Ok(ModuleHandleIndex(
*self
.module_handles
.get(self.module_handle(module_name)?)
.unwrap(),
))
}
pub fn field_handle_index(
&mut self,
owner: StructDefinitionIndex,
field: u16,
) -> Result<FieldHandleIndex> {
let field_handle = FieldHandle { owner, field };
Ok(FieldHandleIndex(get_or_add_item(
&mut self.field_handles,
field_handle,
)?))
}
pub fn struct_instantiation_index(
&mut self,
def: StructDefinitionIndex,
type_parameters: SignatureIndex,
) -> Result<StructDefInstantiationIndex> {
let struct_inst = StructDefInstantiation {
def,
type_parameters,
};
Ok(StructDefInstantiationIndex(get_or_add_item(
&mut self.struct_instantiations,
struct_inst,
)?))
}
pub fn function_instantiation_index(
&mut self,
handle: FunctionHandleIndex,
type_parameters: SignatureIndex,
) -> Result<FunctionInstantiationIndex> {
let func_inst = FunctionInstantiation {
handle,
type_parameters,
};
Ok(FunctionInstantiationIndex(get_or_add_item(
&mut self.function_instantiations,
func_inst,
)?))
}
pub fn field_instantiation_index(
&mut self,
handle: FieldHandleIndex,
type_parameters: SignatureIndex,
) -> Result<FieldInstantiationIndex> {
let field_inst = FieldInstantiation {
handle,
type_parameters,
};
Ok(FieldInstantiationIndex(get_or_add_item(
&mut self.field_instantiations,
field_inst,
)?))
}
pub fn label_index(&mut self, label: BlockLabel_) -> Result<CodeOffset> {
get_or_add_item(&mut self.labels, label)
}
pub fn identifier_index(&mut self, s: impl AsRef<str>) -> Result<IdentifierIndex> {
let ident = ident_str(s.as_ref())?;
let m = &mut self.identifiers;
let idx: Result<TableIndex> = get_or_add_item_macro!(m, ident, ident.to_owned());
Ok(IdentifierIndex(idx?))
}
pub fn address_index(&mut self, addr: AccountAddress) -> Result<AddressIdentifierIndex> {
Ok(AddressIdentifierIndex(get_or_add_item(
&mut self.address_identifiers,
addr,
)?))
}
#[allow(clippy::ptr_arg)]
pub fn constant_index(&mut self, constant: Constant) -> Result<ConstantPoolIndex> {
Ok(ConstantPoolIndex(get_or_add_item(
&mut self.constant_pool,
constant,
)?))
}
pub fn named_constant_index(&mut self, constant: &ConstantName) -> Result<ConstantPoolIndex> {
match self.named_constants.get(constant) {
None => bail!("Missing constant definition for {}", constant),
Some(idx) => Ok(ConstantPoolIndex(*idx)),
}
}
pub fn field(
&self,
s: StructHandleIndex,
f: Field_,
) -> Result<(StructDefinitionIndex, SignatureToken, usize)> {
match self.fields.get(&(s, f.clone())) {
None => bail!("Unbound field {}", f),
Some((sd_idx, token, decl_order)) => Ok((*sd_idx, token.clone(), *decl_order)),
}
}
pub fn struct_definition_index(&self, s: &StructName) -> Result<StructDefinitionIndex> {
match self.struct_defs.get(s) {
None => bail!("Missing struct definition for {}", s),
Some(idx) => Ok(StructDefinitionIndex(*idx)),
}
}
pub fn signature_index(&mut self, sig: Signature) -> Result<SignatureIndex> {
Ok(SignatureIndex(get_or_add_item(&mut self.signatures, sig)?))
}
pub fn set_function_index(&mut self, index: TableIndex) {
self.current_function_index = FunctionDefinitionIndex(index);
}
pub fn current_function_definition_index(&self) -> FunctionDefinitionIndex {
self.current_function_index
}
pub fn current_struct_definition_index(&self) -> StructDefinitionIndex {
let idx = self.struct_defs.len();
StructDefinitionIndex(idx as TableIndex)
}
pub fn declare_friend(&mut self, id: ModuleIdent) -> Result<ModuleHandle> {
let address = self.address_index(id.address)?;
let name = self.identifier_index(id.name.0)?;
Ok(ModuleHandle { address, name })
}
pub fn declare_import(
&mut self,
id: ModuleIdent,
alias: ModuleName,
) -> Result<ModuleHandleIndex> {
self.aliases.insert(id, alias);
let address = self.address_index(id.address)?;
let name = self.identifier_index(id.name.0)?;
self.modules
.insert(alias, (id, ModuleHandle { address, name }));
Ok(ModuleHandleIndex(get_or_add_item_ref(
&mut self.module_handles,
&self.modules.get(&alias).unwrap().1,
)?))
}
pub fn declare_struct_handle_index(
&mut self,
sname: QualifiedStructIdent,
abilities: AbilitySet,
type_parameters: Vec<StructTypeParameter>,
) -> Result<StructHandleIndex> {
self.declare_struct_handle_index_with_abilities(sname, abilities, type_parameters)
}
fn declare_struct_handle_index_with_abilities(
&mut self,
sname: QualifiedStructIdent,
abilities: AbilitySet,
type_parameters: Vec<StructTypeParameter>,
) -> Result<StructHandleIndex> {
let module = self.module_handle_index(&sname.module)?;
let name = self.identifier_index(sname.name.0)?;
self.structs.insert(
sname.clone(),
StructHandle {
module,
name,
abilities,
type_parameters,
},
);
Ok(StructHandleIndex(get_or_add_item_ref(
&mut self.struct_handles,
self.structs.get(&sname).unwrap(),
)?))
}
pub fn declare_struct_definition_index(
&mut self,
s: StructName,
) -> Result<StructDefinitionIndex> {
let idx = self.struct_defs.len();
if idx > TABLE_MAX_SIZE {
bail!("too many struct definitions {}", s)
}
Ok(StructDefinitionIndex(
*self.struct_defs.entry(s).or_insert(idx as TableIndex),
))
}
pub fn declare_function(
&mut self,
mname: ModuleName,
fname: FunctionName,
signature: FunctionSignature,
) -> Result<()> {
let m_f = (mname, fname.clone());
let module = self.module_handle_index(&mname)?;
let name = self.identifier_index(fname.0)?;
self.function_signatures
.insert(m_f.clone(), signature.clone());
let FunctionSignature {
return_,
parameters,
type_parameters,
} = signature;
let params_idx = get_or_add_item(&mut self.signatures, Signature(parameters))?;
let return_idx = get_or_add_item(&mut self.signatures, Signature(return_))?;
let handle = FunctionHandle {
module,
name,
parameters: SignatureIndex(params_idx as TableIndex),
return_: SignatureIndex(return_idx as TableIndex),
type_parameters,
};
let hidx = match self.function_handles.get(&m_f) {
None => self.function_handles.len(),
Some((_, idx)) => idx.0 as usize,
};
if hidx > TABLE_MAX_SIZE {
bail!("too many functions: {}.{}", mname, fname)
}
let handle_index = FunctionHandleIndex(hidx as TableIndex);
self.function_handles.insert(m_f, (handle, handle_index));
Ok(())
}
pub fn declare_constant(&mut self, name: ConstantName, constant: Constant) -> Result<()> {
let idx = self.constant_index(constant)?;
self.named_constants.insert(name, idx.0);
Ok(())
}
pub fn declare_field(
&mut self,
s: StructHandleIndex,
sd_idx: StructDefinitionIndex,
f: Field_,
token: SignatureToken,
decl_order: usize,
) {
self.fields
.entry((s, f))
.or_insert((sd_idx, token, decl_order));
}
fn dependency(&self, m: &ModuleIdent) -> Result<&CompiledDependencyView> {
let dep = self
.dependencies
.get(m)
.ok_or_else(|| format_err!("Dependency not provided for {}", m))?;
Ok(match dep {
CompiledDependency::Borrowed(v) => v,
CompiledDependency::Stored(stored) => stored.borrow_view(),
})
}
fn dep_struct_handle(
&mut self,
s: &QualifiedStructIdent,
) -> Result<(AbilitySet, Vec<StructTypeParameter>)> {
if s.module == ModuleName::module_self() {
bail!("Unbound struct {}", s)
}
let mident = *self.module_ident(&s.module)?;
let dep = self.dependency(&mident)?;
match dep.struct_handle(&mident.name, &s.name) {
None => bail!("Unbound struct {}", s),
Some(shandle) => Ok((shandle.abilities, shandle.type_parameters.clone())),
}
}
pub fn struct_handle_index(&mut self, s: QualifiedStructIdent) -> Result<StructHandleIndex> {
match self.structs.get(&s) {
Some(sh) => Ok(StructHandleIndex(*self.struct_handles.get(sh).unwrap())),
None => {
let (abilities, type_parameters) = self.dep_struct_handle(&s)?;
self.declare_struct_handle_index_with_abilities(s, abilities, type_parameters)
}
}
}
fn reindex_signature_token(
&mut self,
dep: &ModuleIdent,
orig: SignatureToken,
) -> Result<SignatureToken> {
Ok(match orig {
x @ SignatureToken::Bool
| x @ SignatureToken::U8
| x @ SignatureToken::U64
| x @ SignatureToken::U128
| x @ SignatureToken::Address
| x @ SignatureToken::Signer
| x @ SignatureToken::TypeParameter(_) => x,
SignatureToken::Vector(inner) => {
let correct_inner = self.reindex_signature_token(dep, *inner)?;
SignatureToken::Vector(Box::new(correct_inner))
}
SignatureToken::Reference(inner) => {
let correct_inner = self.reindex_signature_token(dep, *inner)?;
SignatureToken::Reference(Box::new(correct_inner))
}
SignatureToken::MutableReference(inner) => {
let correct_inner = self.reindex_signature_token(dep, *inner)?;
SignatureToken::MutableReference(Box::new(correct_inner))
}
SignatureToken::Struct(orig_sh_idx) => {
let dep_info = self.dependency(dep)?;
let (mident, sname) = dep_info
.source_struct_info(orig_sh_idx)
.ok_or_else(|| format_err!("Malformed dependency"))?;
let module_name = *self.module_alias(&mident)?;
let sident = QualifiedStructIdent {
module: module_name,
name: sname,
};
let correct_sh_idx = self.struct_handle_index(sident)?;
SignatureToken::Struct(correct_sh_idx)
}
SignatureToken::StructInstantiation(orig_sh_idx, inners) => {
let dep_info = self.dependency(dep)?;
let (mident, sname) = dep_info
.source_struct_info(orig_sh_idx)
.ok_or_else(|| format_err!("Malformed dependency"))?;
let module_name = *self.module_alias(&mident)?;
let sident = QualifiedStructIdent {
module: module_name,
name: sname,
};
let correct_sh_idx = self.struct_handle_index(sident)?;
let correct_inners = inners
.into_iter()
.map(|t| self.reindex_signature_token(dep, t))
.collect::<Result<_>>()?;
SignatureToken::StructInstantiation(correct_sh_idx, correct_inners)
}
})
}
fn reindex_function_signature(
&mut self,
dep: &ModuleIdent,
orig: FunctionSignature,
) -> Result<FunctionSignature> {
let return_ = orig
.return_
.into_iter()
.map(|t| self.reindex_signature_token(dep, t))
.collect::<Result<_>>()?;
let parameters = orig
.parameters
.into_iter()
.map(|t| self.reindex_signature_token(dep, t))
.collect::<Result<_>>()?;
let type_parameters = orig.type_parameters;
Ok(FunctionSignature {
return_,
parameters,
type_parameters,
})
}
fn dep_function_signature(
&mut self,
m: &ModuleName,
f: &FunctionName,
) -> Result<FunctionSignature> {
if m == &ModuleName::module_self() {
bail!("Unbound function {}.{}", m, f)
}
let mident = *self.module_ident(m)?;
let dep = self.dependency(&mident)?;
match dep.function_signature(f) {
None => bail!("Unbound function {}.{}", mident, f),
Some(sig) => self.reindex_function_signature(&mident, sig),
}
}
fn ensure_function_declared(&mut self, m: ModuleName, f: FunctionName) -> Result<()> {
let m_f = (m, f.clone());
if !self.function_handles.contains_key(&m_f) {
assert!(!self.function_signatures.contains_key(&m_f));
let sig = self.dep_function_signature(&m, &f)?;
self.declare_function(m, f, sig)?;
}
assert!(self.function_handles.contains_key(&m_f));
assert!(self.function_signatures.contains_key(&m_f));
Ok(())
}
pub fn function_handle(
&mut self,
m: ModuleName,
f: FunctionName,
) -> Result<&(FunctionHandle, FunctionHandleIndex)> {
self.ensure_function_declared(m, f.clone())?;
Ok(self.function_handles.get(&(m, f)).unwrap())
}
pub fn decl_location(&self) -> Loc {
self.source_map.definition_location
}
}