Skip to content

Commit

Permalink
improved function resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
brockelmore committed Jul 19, 2024
1 parent 164d47e commit 1128db4
Show file tree
Hide file tree
Showing 22 changed files with 1,103 additions and 701 deletions.
1 change: 1 addition & 0 deletions crates/graph/src/graph_elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub enum Node {
Block(Block),
/// A yul-based function
YulFunction(YulFunction),
// TODO: Handle events
}

pub fn as_dot_str(
Expand Down
76 changes: 75 additions & 1 deletion crates/graph/src/nodes/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ impl BuiltInNode {
}

/// Checks if this builtin is implicitly castable to another builtin
pub fn castable_to(
&self,
other: &Self,
analyzer: &impl GraphBackend,
) -> Result<bool, GraphError> {
Ok(self
.underlying(analyzer)?
.castable_to(other.underlying(analyzer)?))
}

pub fn implicitly_castable_to(
&self,
other: &Self,
Expand Down Expand Up @@ -219,6 +229,44 @@ impl Builtin {
builtins
}

pub fn possible_upcast_builtins(&self) -> Vec<Builtin> {
let mut builtins = vec![];
match self {
Builtin::Uint(size) => {
let mut s = *size;
while s <= 256 {
builtins.push(Builtin::Uint(s));
s += 8;
}
}
Builtin::Int(size) => {
let mut s = *size;
while s <= 256 {
builtins.push(Builtin::Int(s));
s += 8;
}
}
Builtin::Bytes(size) => {
let mut s = *size;
while s <= 32 {
builtins.push(Builtin::Bytes(s));
s += 1;
}
}
Builtin::Address => builtins.push(Builtin::Address),
Builtin::AddressPayable => {
builtins.push(Builtin::Address);
builtins.push(Builtin::AddressPayable);
}
Builtin::Payable => {
builtins.push(Builtin::Address);
builtins.push(Builtin::AddressPayable);
}
_ => {}
}
builtins
}

/// Construct a [`SolcRange`] that is zero
pub fn zero_range(&self) -> Option<SolcRange> {
match self {
Expand Down Expand Up @@ -365,7 +413,7 @@ impl Builtin {
}

/// Checks if self is implicitly castable to another builtin
pub fn implicitly_castable_to(&self, other: &Self) -> bool {
pub fn castable_to(&self, other: &Self) -> bool {
use Builtin::*;
match (self, other) {
(Address, Address) => true,
Expand All @@ -389,6 +437,32 @@ impl Builtin {
}
}

/// Checks if self is implicitly castable to another builtin
pub fn implicitly_castable_to(&self, other: &Self) -> bool {
use Builtin::*;

let res = match (self, other) {
(Address, Address) => true,
(AddressPayable, Address) => true,
(AddressPayable, Payable) => true,
(AddressPayable, AddressPayable) => true,
(Payable, Address) => true,
(Payable, AddressPayable) => true,
(Payable, Payable) => true,
(Bool, Bool) => true,
(Rational, Rational) => true,
(DynamicBytes, DynamicBytes) => true,
(String, String) => true,
(Uint(from_size), Uint(to_size)) => from_size <= to_size,
(Int(from_size), Int(to_size)) => from_size <= to_size,
(Bytes(from_size), Bytes(to_size)) => from_size <= to_size,
_ => false,
};

println!("{self:?} implicitly castable to {other:?}, res: {res:?}");
res
}

/// Returns the max size version of this builtin
pub fn max_size(&self) -> Self {
use Builtin::*;
Expand Down
28 changes: 28 additions & 0 deletions crates/graph/src/nodes/concrete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,34 @@ impl Concrete {
}
}

pub fn alt_lit_builtins(&self) -> Vec<Builtin> {
let mut alts = vec![];
if let Concrete::Uint(size, val) = self {
// literal(u160) -> address
if *size == 160 {
alts.push(Builtin::Address);
}

// uint -> int, all size steps between
let mut new_size = *size;
let imax = U256::from(2).pow((*size - 1).into());
// we may have to bump size by 8 bits
if val > &imax {
new_size += 8;
}
// if a valid
while new_size <= 256 {
alts.push(Builtin::Int(new_size));
new_size += 8;
}

// exact bytesX
let bytes_size = size / 8;
alts.push(Builtin::Bytes(bytes_size as u8));
}
alts
}

/// Converts a concrete into a `U256`.
pub fn into_u256(&self) -> Option<U256> {
match self {
Expand Down
13 changes: 13 additions & 0 deletions crates/graph/src/nodes/context/expr_ret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,17 @@ impl ExprRet {
panic!("Expected a Vec of length {} but it was {}", N, v.len())
})
}

pub fn implicitly_castable_to(
&self,
analyzer: &impl GraphBackend,
to_ty: &VarType,
) -> Result<bool, GraphError> {
let idx = self.expect_single()?;
let is_lit = self.has_literal();
let Some(self_ty) = VarType::try_from_idx(analyzer, idx) else {
return Ok(false);
};
self_ty.implicitly_castable_to(to_ty, analyzer, is_lit)
}
}
2 changes: 1 addition & 1 deletion crates/graph/src/nodes/context/querying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl ContextNode {
return Ok(vis.clone());
}
if let Some(contract) = self.maybe_associated_contract(analyzer)? {
let mut mapping = contract.linearized_functions(analyzer)?;
let mut mapping = contract.linearized_functions(analyzer, false)?;
// extend with free floating functions
mapping.extend(
analyzer
Expand Down
101 changes: 72 additions & 29 deletions crates/graph/src/nodes/contract_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ impl ContractNode {
.push(ContractNode::from(*found));
analyzer.add_edge(*found, *self, Edge::InheritedContract);
});
self.order_inherits(analyzer);
}

pub fn order_inherits(&self, analyzer: &mut impl AnalyzerBackend) {
let raw_inherits = self.underlying(analyzer).unwrap().raw_inherits.clone();
let inherits = self.underlying(analyzer).unwrap().inherits.clone();

let mut tmp_inherits = vec![];
tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0)));
inherits.into_iter().for_each(|inherited| {
let i_name = inherited.name(analyzer).unwrap();
let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap();
tmp_inherits[position] = inherited;
});
self.underlying_mut(analyzer).unwrap().inherits = tmp_inherits;
}

pub fn direct_inherited_contracts(&self, analyzer: &impl GraphBackend) -> Vec<ContractNode> {
Expand Down Expand Up @@ -215,27 +230,34 @@ impl ContractNode {
pub fn linearized_functions(
&self,
analyzer: &mut (impl Search + AnalyzerBackend),
super_func: bool,
) -> Result<BTreeMap<String, FunctionNode>, GraphError> {
if let Some(funcs) = &self.underlying(analyzer)?.cached_functions {
Ok(funcs.clone())
} else {
let mut mapping = self.funcs_mapping(analyzer);
self.direct_inherited_contracts(analyzer)
.iter()
.for_each(|inherited| {
inherited
.linearized_functions(analyzer)
.unwrap()
.iter()
.for_each(|(name, func)| {
if !mapping.contains_key(name) {
mapping.insert(name.to_string(), *func);
}
});
});
self.underlying_mut(analyzer)?.cached_functions = Some(mapping.clone());
Ok(mapping)
if !super_func {
if let Some(funcs) = &self.underlying(analyzer)?.cached_functions {
return Ok(funcs.clone());
}
}

let mut mapping = if !super_func {
self.funcs_mapping(analyzer)
} else {
Default::default()
};
self.direct_inherited_contracts(analyzer)
.iter()
.for_each(|inherited| {
inherited
.linearized_functions(analyzer, false)
.unwrap()
.iter()
.for_each(|(name, func)| {
if !mapping.contains_key(name) {
mapping.insert(name.to_string(), *func);
}
});
});
self.underlying_mut(analyzer)?.cached_functions = Some(mapping.clone());
Ok(mapping)
}

pub fn structs(&self, analyzer: &(impl GraphBackend + Search)) -> Vec<StructNode> {
Expand Down Expand Up @@ -359,6 +381,8 @@ pub struct Contract {
pub ty: ContractTy,
/// An optional name in the form of an identifier (`(Loc, String)`)
pub name: Option<Identifier>,
///
pub raw_inherits: Vec<String>,
/// A list of contracts that this contract inherits (TODO: inheritance linearization)
pub inherits: Vec<ContractNode>,
/// Cached linearized functions
Expand All @@ -379,10 +403,12 @@ impl Contract {
imports: &[Option<NodeIdx>],
analyzer: &impl GraphBackend,
) -> (Contract, Vec<String>) {
let mut raw_inherits = vec![];
let mut inherits = vec![];
let mut unhandled_inherits = vec![];
con.base.iter().for_each(|base| {
let inherited_name = &base.name.identifiers[0].name;
raw_inherits.push(inherited_name.clone());
let mut found = false;
for contract in analyzer
.search_children_exclude_via(source, &Edge::Contract, &[Edge::Func])
Expand Down Expand Up @@ -416,15 +442,32 @@ impl Contract {
unhandled_inherits.push(inherited_name.clone());
}
});
(
Contract {
loc: con.loc,
ty: con.ty,
name: con.name,
inherits,
cached_functions: None,
},
unhandled_inherits,
)

raw_inherits.reverse();
let mut this = Contract {
loc: con.loc,
ty: con.ty,
name: con.name,
raw_inherits,
inherits,
cached_functions: None,
};

this.order_inherits(analyzer);
(this, unhandled_inherits)
}

pub fn order_inherits(&mut self, analyzer: &impl GraphBackend) {
let raw_inherits = self.raw_inherits.clone();
let inherits = self.inherits.clone();

let mut tmp_inherits = vec![];
tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0)));
inherits.into_iter().for_each(|inherited| {
let i_name = inherited.name(analyzer).unwrap();
let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap();
tmp_inherits[position] = inherited;
});
self.inherits = tmp_inherits;
}
}
Loading

0 comments on commit 1128db4

Please sign in to comment.