use delegate::delegate;
use derive_more::From;
use foundry_compilers::artifacts::{
ast::SourceLocation, Block, ContractDefinition, Expression, ForStatement, FunctionDefinition,
ModifierDefinition, SourceUnit, TypeName, UncheckedBlock, VariableDeclaration,
};
use once_cell::sync::OnceCell;
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::analysis::{macros::universal_id, ContractRef, FunctionRef};
pub const EDB_RUNTIME_VALUE_OFFSET: u64 = 0x234c6dfc3bf8fed1;
universal_id! {
UVID => EDB_RUNTIME_VALUE_OFFSET
}
#[derive(Clone, derive_more::Debug)]
#[allow(unused)]
pub struct VariableRef {
inner: Arc<RwLock<Variable>>,
#[debug(ignore)]
name: OnceCell<String>,
#[debug(ignore)]
declaration: OnceCell<VariableDeclaration>,
}
impl From<Variable> for VariableRef {
fn from(variable: Variable) -> Self {
Self::new(variable)
}
}
#[allow(unused)]
impl VariableRef {
pub fn new(inner: Variable) -> Self {
Self {
inner: Arc::new(RwLock::new(inner)),
declaration: OnceCell::new(),
name: OnceCell::new(),
}
}
pub(crate) fn read(&self) -> RwLockReadGuard<'_, Variable> {
self.inner.read()
}
pub(crate) fn write(&self) -> RwLockWriteGuard<'_, Variable> {
self.inner.write()
}
pub fn id(&self) -> UVID {
self.inner.read().id()
}
pub fn declaration(&self) -> &VariableDeclaration {
self.declaration.get_or_init(|| self.inner.read().declaration())
}
pub fn type_name(&self) -> Option<&TypeName> {
self.declaration().type_name.as_ref()
}
pub fn base(&self) -> Self {
let inner = self.inner.read();
if let Some(base) = inner.base() {
base
} else {
self.clone()
}
}
pub fn function(&self) -> Option<FunctionRef> {
self.inner.read().function()
}
pub fn contract(&self) -> Option<ContractRef> {
self.inner.read().contract()
}
}
impl Serialize for VariableRef {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.read().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for VariableRef {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let variable = Variable::deserialize(deserializer)?;
Ok(Self::new(variable))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum Variable {
Plain {
uvid: UVID,
declaration: VariableDeclaration,
state_variable: bool,
function: Option<FunctionRef>,
contract: Option<ContractRef>,
},
Member {
base: VariableRef,
member: String,
},
Index {
base: VariableRef,
index: Expression,
},
IndexRange {
base: VariableRef,
start: Option<Expression>,
end: Option<Expression>,
},
}
impl Variable {
pub fn id(&self) -> UVID {
match self {
Self::Plain { uvid, .. } => *uvid,
Self::Member { base, .. } => base.read().id(),
Self::Index { base, .. } => base.read().id(),
Self::IndexRange { base, .. } => base.read().id(),
}
}
pub fn declaration(&self) -> VariableDeclaration {
match self {
Self::Plain { declaration, .. } => declaration.clone(),
Self::Member { base, .. } => base.read().declaration(),
Self::Index { base, .. } => base.read().declaration(),
Self::IndexRange { base, .. } => base.read().declaration(),
}
}
pub fn function(&self) -> Option<FunctionRef> {
match self {
Self::Plain { function, .. } => function.clone(),
Self::Member { base, .. } => base.read().function(),
Self::Index { base, .. } => base.read().function(),
Self::IndexRange { base, .. } => base.read().function(),
}
}
pub fn contract(&self) -> Option<ContractRef> {
match self {
Self::Plain { contract, .. } => contract.clone(),
Self::Member { base, .. } => base.read().contract(),
Self::Index { base, .. } => base.read().contract(),
Self::IndexRange { base, .. } => base.read().contract(),
}
}
pub fn base(&self) -> Option<VariableRef> {
match self {
Self::Plain { .. } => None,
Self::Member { base, .. }
| Self::Index { base, .. }
| Self::IndexRange { base, .. } => {
if let Some(base) = base.read().base() {
Some(base)
} else {
Some(base.clone())
}
}
}
}
pub fn pretty_display(&self) -> String {
match self {
Self::Plain { declaration, .. } => declaration.name.clone(),
Self::Member { base, member } => format!("{}.{}", base.read().pretty_display(), member),
Self::Index { base, .. } => format!("{}[.]", base.read().pretty_display()),
Self::IndexRange { base, .. } => {
format!("{}[..]", base.read().pretty_display())
}
}
}
}
#[derive(Clone, derive_more::Debug)]
pub struct VariableScopeRef {
inner: Arc<RwLock<VariableScope>>,
#[debug(ignore)]
children: OnceCell<Vec<VariableScopeRef>>,
#[debug(ignore)]
variables: OnceCell<Vec<VariableRef>>,
#[debug(ignore)]
variables_recursive: OnceCell<Vec<VariableRef>>,
}
impl From<VariableScope> for VariableScopeRef {
fn from(scope: VariableScope) -> Self {
Self::new(scope)
}
}
impl VariableScopeRef {
pub fn new(inner: VariableScope) -> Self {
Self {
inner: Arc::new(RwLock::new(inner)),
variables_recursive: OnceCell::new(),
variables: OnceCell::new(),
children: OnceCell::new(),
}
}
pub(crate) fn read(&self) -> RwLockReadGuard<'_, VariableScope> {
self.inner.read()
}
pub(crate) fn write(&self) -> RwLockWriteGuard<'_, VariableScope> {
self.inner.write()
}
}
impl VariableScopeRef {
delegate! {
to self.inner.read() {
pub fn ast_id(&self) -> usize;
pub fn src(&self) -> SourceLocation;
pub fn pretty_display(&self) -> String;
}
}
}
impl VariableScopeRef {
pub fn clear_cache(&mut self) {
self.variables_recursive.take();
self.variables.take();
self.children.take();
}
pub fn children(&self) -> &Vec<Self> {
self.children.get_or_init(|| self.inner.read().children.clone())
}
pub fn variables(&self) -> &Vec<VariableRef> {
self.variables.get_or_init(|| self.inner.read().variables.clone())
}
pub fn variables_recursive(&self) -> &Vec<VariableRef> {
self.variables_recursive.get_or_init(|| {
let mut variables = self.variables().clone();
variables.extend(
self.inner
.read()
.parent
.as_ref()
.map_or(vec![], |parent| parent.variables_recursive().clone()),
);
variables
})
}
}
impl Serialize for VariableScopeRef {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.read().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for VariableScopeRef {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let scope = VariableScope::deserialize(deserializer)?;
Ok(Self::new(scope))
}
}
#[derive(Clone, Serialize, Deserialize, derive_more::Debug)]
#[non_exhaustive]
pub struct VariableScope {
pub node: ScopeNode,
pub variables: Vec<VariableRef>,
pub parent: Option<VariableScopeRef>,
pub children: Vec<VariableScopeRef>,
}
impl VariableScope {
pub fn ast_id(&self) -> usize {
self.node.ast_id()
}
pub fn src(&self) -> SourceLocation {
self.node.src()
}
pub fn variables_recursive(&self) -> Vec<VariableRef> {
let mut variables = self.variables.clone();
variables.extend(
self.parent.clone().map_or(vec![], |parent| parent.read().variables_recursive()),
);
variables
}
pub fn pretty_display(&self) -> String {
self.pretty_display_with_indent(0)
}
fn pretty_display_with_indent(&self, indent_level: usize) -> String {
let mut result = String::new();
let indent = " ".repeat(indent_level);
if self.variables.is_empty() {
result.push_str(&format!("{}Scope({}): {{}}", indent, self.node.variant_name()));
} else {
let mut variable_names: Vec<String> =
self.variables.iter().map(|var| var.read().pretty_display()).collect();
variable_names.sort(); result.push_str(&format!(
"{}Scope({}): {{{}}}",
indent,
self.node.variant_name(),
variable_names.join(", ")
));
}
for child in &self.children {
result.push('\n');
result.push_str(&child.read().pretty_display_with_indent(indent_level + 1));
}
result
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum VariableType {
Uint(u8),
Address,
Bool,
}
#[derive(Debug, Clone, From, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
pub enum ScopeNode {
SourceUnit(#[from] SourceUnit),
Block(#[from] Block),
UncheckedBlock(#[from] UncheckedBlock),
ForStatement(#[from] ForStatement),
ContractDefinition(#[from] ContractDefinition),
FunctionDefinition(#[from] FunctionDefinition),
ModifierDefinition(#[from] ModifierDefinition),
}
impl ScopeNode {
pub fn ast_id(&self) -> usize {
match self {
Self::SourceUnit(source_unit) => source_unit.id,
Self::Block(block) => block.id,
Self::UncheckedBlock(unchecked_block) => unchecked_block.id,
Self::ForStatement(for_statement) => for_statement.id,
Self::ContractDefinition(contract_definition) => contract_definition.id,
Self::FunctionDefinition(function_definition) => function_definition.id,
Self::ModifierDefinition(modifier_definition) => modifier_definition.id,
}
}
pub fn src(&self) -> SourceLocation {
match self {
Self::SourceUnit(source_unit) => source_unit.src,
Self::Block(block) => block.src,
Self::UncheckedBlock(unchecked_block) => unchecked_block.src,
Self::ForStatement(for_statement) => for_statement.src,
Self::ContractDefinition(contract_definition) => contract_definition.src,
Self::FunctionDefinition(function_definition) => function_definition.src,
Self::ModifierDefinition(modifier_definition) => modifier_definition.src,
}
}
pub fn variant_name(&self) -> &'static str {
match self {
Self::SourceUnit(_) => "SourceUnit",
Self::Block(_) => "Block",
Self::UncheckedBlock(_) => "UncheckedBlock",
Self::ForStatement(_) => "ForStatement",
Self::ContractDefinition(_) => "ContractDefinition",
Self::FunctionDefinition(_) => "FunctionDefinition",
Self::ModifierDefinition(_) => "ModifierDefinition",
}
}
}