use crate::parse_tree::Purity;
use sway_ir::{Context, MetadataIndex, Metadatum, Value};
use sway_types::Span;
use std::{collections::HashMap, path::PathBuf, sync::Arc};
#[derive(Default)]
pub(crate) struct MetadataManager {
md_span_cache: HashMap<MetadataIndex, Span>,
md_file_loc_cache: HashMap<MetadataIndex, (Arc<PathBuf>, Arc<str>)>,
md_storage_op_cache: HashMap<MetadataIndex, StorageOperation>,
md_storage_key_cache: HashMap<MetadataIndex, u64>,
span_md_cache: HashMap<Span, MetadataIndex>,
file_loc_md_cache: HashMap<*const PathBuf, MetadataIndex>,
storage_op_md_cache: HashMap<Purity, MetadataIndex>,
storage_key_md_cache: HashMap<u64, MetadataIndex>,
}
#[derive(Clone, Copy)]
pub(crate) enum StorageOperation {
Reads,
Writes,
ReadsWrites,
}
impl MetadataManager {
pub(crate) fn md_to_span(
&mut self,
context: &Context,
md_idx: Option<MetadataIndex>,
) -> Option<Span> {
Self::for_each_md_idx(context, md_idx, |md_idx| {
self.md_span_cache.get(&md_idx).cloned().or_else(|| {
context.metadata[md_idx.0]
.unwrap_struct("span", 3)
.and_then(|fields| {
let (path, src) = self.md_to_file_location(context, &fields[0])?;
let start = fields[1].unwrap_integer()?;
let end = fields[2].unwrap_integer()?;
let span = Span::new(src, start as usize, end as usize, Some(path))?;
self.md_span_cache.insert(md_idx, span.clone());
Some(span)
})
})
})
}
pub(crate) fn md_to_storage_op(
&mut self,
context: &Context,
md_idx: Option<MetadataIndex>,
) -> Option<StorageOperation> {
Self::for_each_md_idx(context, md_idx, |md_idx| {
self.md_storage_op_cache.get(&md_idx).copied().or_else(|| {
context.metadata[md_idx.0]
.unwrap_struct("storage", 1)
.and_then(|fields| {
fields[0].unwrap_string().and_then(|stor_str| {
let op = match stor_str {
"reads" => Some(StorageOperation::Reads),
"writes" => Some(StorageOperation::Writes),
"readswrites" => Some(StorageOperation::ReadsWrites),
_otherwise => None,
}?;
self.md_storage_op_cache.insert(md_idx, op);
Some(op)
})
})
})
})
}
pub(crate) fn md_to_storage_key(
&mut self,
context: &Context,
md_idx: Option<MetadataIndex>,
) -> Option<u64> {
Self::for_each_md_idx(context, md_idx, |md_idx| {
self.md_storage_key_cache.get(&md_idx).copied().or_else(|| {
context.metadata[md_idx.0]
.unwrap_struct("state_index", 1)
.and_then(|fields| {
let key = fields[0].unwrap_integer()?;
self.md_storage_key_cache.insert(md_idx, key);
Some(key)
})
})
})
}
fn md_to_file_location(
&mut self,
context: &Context,
md: &Metadatum,
) -> Option<(Arc<PathBuf>, Arc<str>)> {
md.unwrap_index().and_then(|md_idx| {
self.md_file_loc_cache.get(&md_idx).cloned().or_else(|| {
context.metadata[md_idx.0]
.unwrap_string()
.and_then(|path_buf_str| {
let path_buf = PathBuf::from(path_buf_str);
let src = std::fs::read_to_string(&path_buf).ok()?;
let path_and_src = (Arc::new(path_buf), Arc::from(src));
self.md_file_loc_cache.insert(md_idx, path_and_src.clone());
Some(path_and_src)
})
})
})
}
pub(crate) fn val_to_span(&mut self, context: &Context, value: Value) -> Option<Span> {
self.md_to_span(context, context.values[value.0].metadata)
}
pub(crate) fn val_to_storage_key(&mut self, context: &Context, value: Value) -> Option<u64> {
self.md_to_storage_key(context, context.values[value.0].metadata)
}
pub(crate) fn span_to_md(
&mut self,
context: &mut Context,
span: &Span,
) -> Option<MetadataIndex> {
self.span_md_cache.get(span).copied().or_else(|| {
span.path().and_then(|path_buf| {
let file_location_md_idx = self.file_location_to_md(context, path_buf)?;
let md_idx = MetadataIndex(context.metadata.insert(Metadatum::Struct(
"span".to_owned(),
vec![
Metadatum::Index(file_location_md_idx),
Metadatum::Integer(span.start() as u64),
Metadatum::Integer(span.end() as u64),
],
)));
self.span_md_cache.insert(span.clone(), md_idx);
Some(md_idx)
})
})
}
pub(crate) fn storage_key_to_md(
&mut self,
context: &mut Context,
storage_key: u64,
) -> Option<MetadataIndex> {
self.storage_key_md_cache
.get(&storage_key)
.copied()
.or_else(|| {
let md_idx = MetadataIndex(context.metadata.insert(Metadatum::Struct(
"state_index".to_owned(),
vec![Metadatum::Integer(storage_key)],
)));
self.storage_key_md_cache.insert(storage_key, md_idx);
Some(md_idx)
})
}
pub(crate) fn purity_to_md(
&mut self,
context: &mut Context,
purity: Purity,
) -> Option<MetadataIndex> {
(purity != Purity::Pure).then(|| {
self.storage_op_md_cache
.get(&purity)
.copied()
.unwrap_or_else(|| {
let field = match purity {
Purity::Pure => unreachable!("Already checked for Pure above."),
Purity::Reads => "reads",
Purity::Writes => "writes",
Purity::ReadsWrites => "readswrites",
};
let md_idx = MetadataIndex(context.metadata.insert(Metadatum::Struct(
"storage".to_owned(),
vec![Metadatum::String(field.to_owned())],
)));
self.storage_op_md_cache.insert(purity, md_idx);
md_idx
})
})
}
fn file_location_to_md(
&mut self,
context: &mut Context,
path: &Arc<PathBuf>,
) -> Option<MetadataIndex> {
self.file_loc_md_cache
.get(&Arc::as_ptr(path))
.copied()
.or_else(|| {
let md_idx = MetadataIndex(
context
.metadata
.insert(Metadatum::String(path.to_string_lossy().into())),
);
self.file_loc_md_cache.insert(Arc::as_ptr(path), md_idx);
Some(md_idx)
})
}
fn for_each_md_idx<T, F: FnMut(MetadataIndex) -> Option<T>>(
context: &Context,
md_idx: Option<MetadataIndex>,
mut f: F,
) -> Option<T> {
md_idx.and_then(|md_idx| {
if let Metadatum::List(md_idcs) = &context.metadata[md_idx.0] {
md_idcs.iter().find_map(|md_idx| f(*md_idx))
} else {
f(md_idx)
}
})
}
}