diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 311b2945..7d7ce65f 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -103,6 +103,8 @@ pub enum Node { Msg(Msg), /// The `block` global in solidity Block(Block), + /// A yul-based function + YulFunction(YulFunction), } pub fn as_dot_str( @@ -276,6 +278,8 @@ pub enum Edge { BuiltinFunction, /// A connection from one contract to another contract UsingContract(Loc), + /// Yul function + YulFunction(usize), } impl Heirarchical for Edge { @@ -286,8 +290,8 @@ impl Heirarchical for Edge { Part | Import => 1, Contract | Ty | Field | Enum | Struct | Error | Event | Var | InheritedContract - | Modifier | FallbackFunc | Constructor | ReceiveFunc | LibraryFunction(_) - | BuiltinFunction | Func | UsingContract(_) => 2, + | Modifier | FallbackFunc | Constructor | YulFunction(_) | ReceiveFunc + | LibraryFunction(_) | BuiltinFunction | Func | UsingContract(_) => 2, Context(_) | ErrorParam | FunctionParam | FunctionReturn | FuncModifier(_) => 3, } diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index f207c1b7..cc4db8db 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -1,7 +1,7 @@ use crate::{ nodes::{ Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, SourceUnitNode, - SourceUnitPartNode, + SourceUnitPartNode, YulFunctionNode, }, range::elem::Elem, AnalyzerBackend, AsDotStr, ContextEdge, Edge, GraphBackend, Node, SolcRange, VarType, @@ -53,6 +53,23 @@ impl FunctionNode { Ok(()) } + pub fn yul_funcs( + &self, + analyzer: &impl GraphBackend, + assembly_block_idx: usize, + ) -> Vec { + analyzer + .graph() + .edges_directed(self.0.into(), Direction::Incoming) + .filter_map(|edge| match edge.weight() { + Edge::YulFunction(asm_block) if *asm_block == assembly_block_idx => { + Some(edge.source().into()) + } + _ => None, + }) + .collect::>() + } + pub fn ty(&self, analyzer: &impl GraphBackend) -> Result { Ok(self.underlying(analyzer)?.ty) } diff --git a/crates/graph/src/nodes/mod.rs b/crates/graph/src/nodes/mod.rs index 5bfc3f86..e380b441 100644 --- a/crates/graph/src/nodes/mod.rs +++ b/crates/graph/src/nodes/mod.rs @@ -42,3 +42,6 @@ pub use source_unit::*; mod debug_reconstruction; pub use debug_reconstruction::*; + +mod yul_func; +pub use yul_func::*; diff --git a/crates/graph/src/nodes/yul_func.rs b/crates/graph/src/nodes/yul_func.rs new file mode 100644 index 00000000..54a30ff6 --- /dev/null +++ b/crates/graph/src/nodes/yul_func.rs @@ -0,0 +1,79 @@ +use crate::{ + nodes::{Concrete, ContractNode}, + range::elem::Elem, + AnalyzerBackend, AsDotStr, Edge, GraphBackend, Node, VarType, +}; + +use shared::{FlatExpr, GraphError, NodeIdx, RangeArena}; + +use petgraph::visit::EdgeRef; +use solang_parser::pt::{Expression, Identifier, Loc, TypeDefinition}; + +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct YulFunctionNode(pub usize); +impl YulFunctionNode { + pub fn underlying<'a>( + &self, + analyzer: &'a impl GraphBackend, + ) -> Result<&'a YulFunction, GraphError> { + match analyzer.node(*self) { + Node::YulFunction(ty) => Ok(ty), + Node::Unresolved(ident) => Err(GraphError::UnknownVariable(format!( + "Could not find variable: {}", + ident.name + ))), + e => Err(GraphError::NodeConfusion(format!( + "Node type confusion: expected node to be YulFunctionNode but it was: {e:?}" + ))), + } + } + + pub fn name(&self, analyzer: &impl GraphBackend) -> Result { + Ok(self.underlying(analyzer)?.name.to_string()) + } + pub fn exprs(&self, analyzer: &impl GraphBackend) -> Result, GraphError> { + Ok(self.underlying(analyzer)?.exprs.clone()) + } +} + +impl From for NodeIdx { + fn from(val: YulFunctionNode) -> Self { + val.0.into() + } +} + +impl From for YulFunctionNode { + fn from(idx: NodeIdx) -> Self { + YulFunctionNode(idx.index()) + } +} + +impl AsDotStr for YulFunctionNode { + fn as_dot_str( + &self, + analyzer: &impl GraphBackend, + _arena: &mut RangeArena>, + ) -> String { + let underlying = self.underlying(analyzer).unwrap(); + format!("yul function {}", underlying.name,) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct YulFunction { + pub loc: Loc, + pub name: &'static str, + pub exprs: Vec, +} + +impl From for Node { + fn from(val: YulFunction) -> Self { + Node::YulFunction(val) + } +} + +impl YulFunction { + pub fn new(exprs: Vec, name: &'static str, loc: Loc) -> YulFunction { + YulFunction { loc, name, exprs } + } +} diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 81d1be68..8d0ae594 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -248,7 +248,8 @@ impl VarType { | Node::Entry | Node::Context(..) | Node::Msg(_) - | Node::Block(_) => None, + | Node::Block(_) + | Node::YulFunction(..) => None, } } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index dec1135f..44465bb1 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -149,6 +149,7 @@ pub struct Analyzer { pub flattened: Vec, pub expr_flag: Option, + pub current_asm_block: usize, } impl<'a> Default for Analyzer { @@ -189,6 +190,7 @@ impl<'a> Default for Analyzer { minimize_debug: None, flattened: vec![], expr_flag: None, + current_asm_block: 0, }; a.builtin_fn_inputs = builtin_fns::builtin_fns_inputs(&mut a); diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 8cf7d4fe..d500cc94 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -564,6 +564,17 @@ impl AnalyzerLike for Analyzer { self.flattened.push(flat); } + fn decrement_asm_block(&mut self) { + self.current_asm_block -= 1; + } + fn increment_asm_block(&mut self) { + self.current_asm_block += 1; + } + + fn current_asm_block(&self) -> usize { + self.current_asm_block + } + fn expr_stack(&self) -> &[FlatExpr] { &self.flattened } diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index 0eb637f5..b2a0d567 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -197,6 +197,9 @@ pub trait AnalyzerLike: GraphLike { type FlatExpr; type ExprFlag: Copy; fn push_expr(&mut self, flat: Self::FlatExpr); + fn increment_asm_block(&mut self); + fn decrement_asm_block(&mut self); + fn current_asm_block(&self) -> usize; fn expr_stack(&self) -> &[Self::FlatExpr]; fn expr_stack_mut(&mut self) -> &mut Vec; fn take_expr_flag(&mut self) -> Option; diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index b36e39d9..3dbfdc8e 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -9,7 +9,7 @@ pub enum ExprFlag { Requirement, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum FlatExpr { VarDef(Loc, Option<&'static str>, Option, bool), If { @@ -108,7 +108,176 @@ pub enum FlatExpr { YulExpr(FlatYulExpr), } +impl std::fmt::Display for FlatExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FlatExpr::*; + match self { + VarDef(_, maybe_name, maybe_storage, inited) => { + let inited_str = if *inited { " = ..;" } else { ";" }; + let name_str = if let Some(name) = maybe_name { + name + } else { + "" + }; + let storage_str = if let Some(stor) = maybe_storage { + format!("{stor} ") + } else { + "".to_string() + }; + write!(f, "{storage_str}{name_str}{inited_str}") + } + Super(_, name) => write!(f, "super.{name}"), + Continue(..) => write!(f, "continue;"), + Break(..) => write!(f, "break;"), + Return(.., elem) => { + if *elem { + write!(f, "return ..;") + } else { + write!(f, "return;") + } + } + + PostIncrement(..) => write!(f, "(..)++"), + PostDecrement(..) => write!(f, "(..)--"), + PreIncrement(..) => write!(f, "++(..)"), + PreDecrement(..) => write!(f, "--(..)"), + New(..) => write!(f, "new "), + ArrayTy(..) => write!(f, "[]"), + ArrayIndexAccess(..) => write!(f, "[(..)]"), + MemberAccess(_, field) => write!(f, ".{field}"), + FunctionCall(_, n) => write!(f, "({})", "_,".repeat(*n)), + NamedFunctionCall(_, _) => write!(f, "(..)"), + Not(_) => write!(f, "~"), + Negate(_) => write!(f, "-"), + Delete(_) => write!(f, "delete "), + + // binary ops + Power(..) => write!(f, " ** "), + Multiply(..) => write!(f, " * "), + Divide(..) => write!(f, " / "), + Modulo(_) => write!(f, " % "), + Add(..) => write!(f, " + "), + Subtract(..) => write!(f, " - "), + AssignAdd(..) => write!(f, " += "), + AssignSubtract(..) => write!(f, " -= "), + AssignMultiply(..) => write!(f, " *= "), + AssignDivide(..) => write!(f, " /= "), + AssignModulo(_) => write!(f, " %= "), + ShiftLeft(_) => write!(f, " << "), + ShiftRight(_) => write!(f, " >> "), + BitwiseAnd(_) => write!(f, " & "), + BitwiseXor(_) => write!(f, " ^ "), + BitwiseOr(_) => write!(f, " | "), + BitwiseNot(_) => write!(f, "~"), + AssignOr(_) => write!(f, " |= "), + AssignAnd(_) => write!(f, " &= "), + AssignXor(_) => write!(f, " ^= "), + AssignShiftLeft(_) => write!(f, " <<= "), + AssignShiftRight(_) => write!(f, " >>= "), + + // cmp ops + Less(_) => write!(f, " < "), + More(_) => write!(f, " > "), + LessEqual(_) => write!(f, " <= "), + MoreEqual(_) => write!(f, " >= "), + Equal(_) => write!(f, " == "), + NotEqual(_) => write!(f, " != "), + And(_) => write!(f, " && "), + Or(_) => write!(f, " || "), + + Assign(_) => write!(f, " = "), + This(_) => write!(f, "this"), + + BoolLiteral(_, b) => write!(f, "{b}"), + NumberLiteral(_, int, exp, unit) => { + let unit_str = if let Some(unit) = unit { unit } else { "" }; + let e_str = if exp.is_empty() { + "".to_string() + } else { + format!("e{exp}") + }; + write!(f, "{int}{e_str} {unit_str}") + } + RationalNumberLiteral(_, int, frac, exp, unit) => { + let unit_str = if let Some(unit) = unit { unit } else { "" }; + let e_str = if exp.is_empty() { + "".to_string() + } else { + format!("e{exp}") + }; + write!(f, "{int}.{frac}{e_str} {unit_str}") + } + HexNumberLiteral(_, s, _) + | StringLiteral(_, s) + | HexLiteral(_, s) + | AddressLiteral(_, s) + | Variable(_, s) => write!(f, "{s}"), + + YulExpr(yul) => write!(f, "{yul}"), + _ => write!(f, ""), + } + } +} + impl FlatExpr { + pub fn if_debug_str(&self, start: usize, stack: &[FlatExpr]) -> String { + use FlatExpr::*; + let If { + true_cond, + false_cond, + true_body, + false_body, + .. + } = self + else { + unreachable!("Not if") + }; + let true_range = start..start + true_cond; + let true_cond_str = stack[true_range] + .iter() + .map(|i| format!("{i}")) + .collect::>() + .join(" "); + let false_range = start + true_cond..start + true_cond + false_cond; + let false_cond_str = stack[false_range] + .iter() + .map(|i| format!("{i}")) + .collect::>() + .join(" "); + + let true_body_range = + start + true_cond + false_cond..start + true_cond + false_cond + true_body; + let true_body_str = stack[true_body_range] + .iter() + .enumerate() + .map(|(j, i)| { + if matches!(i, If { .. }) { + let new_start = start + true_cond + false_cond + j; + i.if_debug_str(new_start, stack) + } else { + format!("{i}") + } + }) + .collect::>() + .join(" "); + let false_body_range = start + true_cond + false_cond + true_body + ..start + true_cond + false_cond + true_body + false_body; + let false_body_str = stack[false_body_range] + .iter() + .enumerate() + .map(|(j, i)| { + if matches!(i, If { .. }) { + let new_start = start + true_cond + false_cond + true_body + j; + i.if_debug_str(new_start, stack) + } else { + format!("{i}") + } + }) + .collect::>() + .join(" "); + + format!("if ({true_cond_str}) {{\n\t{true_body_str}\n}} else ({false_cond_str}) {{\n\t{false_body_str}\n}}") + } pub fn try_inv_cmp(&self) -> Option { use FlatExpr::*; Some(match self { @@ -195,13 +364,15 @@ impl FlatExpr { | Super(loc, ..) | YulExpr(FlatYulExpr::YulVariable(loc, ..)) | YulExpr(FlatYulExpr::YulFuncCall(loc, ..)) + | YulExpr(FlatYulExpr::YulAssign(loc, ..)) | YulExpr(FlatYulExpr::YulSuffixAccess(loc, ..)) + | YulExpr(FlatYulExpr::YulVarDecl(loc, ..)) + | YulExpr(FlatYulExpr::YulFuncDef(loc, ..)) | ArrayLiteral(loc, ..) => Some(*loc), FunctionCallName(..) | YulExpr(FlatYulExpr::YulStartBlock) - | YulExpr(FlatYulExpr::YulEndBlock) - | YulExpr(FlatYulExpr::YulFunctionCallName(..)) => None, + | YulExpr(FlatYulExpr::YulEndBlock) => None, } } } diff --git a/crates/shared/src/flattened_yul.rs b/crates/shared/src/flattened_yul.rs index e6d4b86b..261d3e23 100644 --- a/crates/shared/src/flattened_yul.rs +++ b/crates/shared/src/flattened_yul.rs @@ -4,16 +4,29 @@ use solang_parser::pt::{ Identifier, Loc, YulExpression, YulFunctionCall, YulStatement, YulSwitchOptions, }; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum FlatYulExpr { YulStartBlock, YulVariable(Loc, &'static str), - YulFunctionCallName(usize), - YulFuncCall(Loc, usize), + YulFuncCall(Loc, &'static str, usize), YulSuffixAccess(Loc, &'static str), + YulAssign(Loc, usize), + YulVarDecl(Loc, usize, bool), + YulFuncDef(Loc, &'static str, usize), YulEndBlock, } +impl std::fmt::Display for FlatYulExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FlatYulExpr::*; + match self { + YulFuncCall(_c, s, n) => write!(f, "{s}({})", "_,".repeat(*n)), + YulVariable(_, s) | YulSuffixAccess(_, s) => write!(f, "{s}"), + _ => write!(f, ""), + } + } +} + impl TryFrom<&YulExpression> for FlatYulExpr { type Error = (); fn try_from(expr: &YulExpression) -> Result { @@ -22,9 +35,11 @@ impl TryFrom<&YulExpression> for FlatYulExpr { Variable(ident) => { FlatYulExpr::YulVariable(ident.loc, string_to_static(ident.name.clone())) } - FunctionCall(yul_func_call) => { - FlatYulExpr::YulFuncCall(yul_func_call.loc, yul_func_call.arguments.len()) - } + FunctionCall(yul_func_call) => FlatYulExpr::YulFuncCall( + yul_func_call.loc, + string_to_static(yul_func_call.id.name.clone()), + yul_func_call.arguments.len(), + ), SuffixAccess(loc, _, ident) => { FlatYulExpr::YulSuffixAccess(*loc, string_to_static(ident.name.clone())) } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 4de9187d..0e9217e6 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,3 +1,4 @@ +use crate::yul::YulFuncCaller; use std::collections::BTreeMap; use crate::{ @@ -9,7 +10,7 @@ use graph::{ elem::{Elem, RangeOp}, nodes::{ Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, - ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, SubContextKind, + ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, SubContextKind, YulFunction, }, AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; @@ -165,7 +166,11 @@ pub trait Flatten: dialect: _, flags: _, block: yul_block, - } => self.traverse_yul_statement(&YulStatement::Block(yul_block.clone())), + } => { + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + self.traverse_yul_statement(&YulStatement::Block(yul_block.clone())); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + } Return(loc, maybe_ret_expr) => { if let Some(ret_expr) = maybe_ret_expr { self.traverse_expression(ret_expr, unchecked); @@ -184,8 +189,37 @@ pub trait Flatten: fn traverse_yul_statement(&mut self, stmt: &YulStatement) { use YulStatement::*; match stmt { - Assign(loc, lhs, rhs) => {} - VariableDeclaration(loc, idents, maybe_assignment) => {} + Assign(loc, lhs, rhs) => { + self.traverse_yul_expression(rhs); + lhs.iter().for_each(|l| { + self.traverse_yul_expression(l); + }); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulAssign(*loc, lhs.len()))); + } + VariableDeclaration(loc, idents, maybe_assignment) => { + if let Some(rhs) = maybe_assignment { + self.traverse_yul_expression(rhs); + } + let uint: &'static solang_parser::pt::Type = + Box::leak(Box::new(solang_parser::pt::Type::Uint(256))); + + idents.iter().for_each(|ident| { + // NOTE: for now yul does not support + // user types. but they may in the future + self.push_expr(FlatExpr::Type(ident.loc, uint)); + self.push_expr(FlatExpr::VarDef( + *loc, + Some(string_to_static(ident.id.name.clone())), + None, + false, + )); + }); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVarDecl( + *loc, + idents.len(), + maybe_assignment.is_some(), + ))); + } If(loc, if_expr, true_stmt) => { let iec = IfElseChain { if_expr: if_expr.clone(), @@ -218,14 +252,45 @@ pub trait Flatten: self.push_expr(FlatExpr::Continue(*loc)); } Block(block) => { - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); for statement in block.statements.iter() { - self.traverse_yul_statement(&statement); + self.traverse_yul_statement(statement); } - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); } FunctionDefinition(def) => { - todo!() + let start_len = self.expr_stack().len(); + let inputs_as_var_decl = + YulStatement::VariableDeclaration(def.loc, def.params.clone(), None); + self.traverse_yul_statement(&inputs_as_var_decl); + + def.params.iter().for_each(|param| { + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVariable( + param.loc, + string_to_static(param.id.name.clone()), + ))) + }); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulAssign( + def.loc, + def.params.len(), + ))); + let rets_as_var_decl = + YulStatement::VariableDeclaration(def.loc, def.returns.clone(), None); + self.traverse_yul_statement(&rets_as_var_decl); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + for stmt in def.body.statements.iter() { + self.traverse_yul_statement(stmt); + } + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + + let func = self.expr_stack_mut().drain(start_len..).collect::>(); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFuncDef( + def.loc, + string_to_static(def.id.name.clone()), + func.len(), + ))); + self.expr_stack_mut().extend(func); } FunctionCall(call) => { self.traverse_yul_expression(&YulExpression::FunctionCall(call.clone())); @@ -237,18 +302,19 @@ pub trait Flatten: fn traverse_yul_if_else(&mut self, loc: Loc, iec: IfElseChain) { let true_body = &iec.true_stmt; let start_len = self.expr_stack_mut().len(); - self.traverse_yul_expression(&iec.if_expr); self.push_expr(FlatExpr::NumberLiteral(loc, "0", "", None)); + self.traverse_yul_expression(&iec.if_expr); + // have it be a require statement self.push_expr(FlatExpr::Requirement(loc)); self.push_expr(FlatExpr::More(loc)); + let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); // the false condition is the same as the true, but with the comparator inverted let mut false_cond = true_cond.clone(); - if let Some(last) = false_cond.pop() { - false_cond.push(last.try_inv_cmp().unwrap_or(FlatExpr::Not(loc))); - } + let _ = false_cond.pop(); + false_cond.push(FlatExpr::Equal(loc)); let true_cond_delta = true_cond.len(); let false_cond_delta = false_cond.len(); @@ -305,16 +371,9 @@ pub trait Flatten: self.traverse_yul_expression(expr); }); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFunctionCallName( - func_call.arguments.len(), - ))); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVariable( - func_call.id.loc, - string_to_static(func_call.id.name.clone()), - ))); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFuncCall( expr.loc(), + string_to_static(func_call.id.name.clone()), func_call.arguments.len(), ))); } @@ -580,7 +639,7 @@ pub trait Flatten: body_loc: Loc, arena: &mut RangeArena>, ) { - let stack = std::mem::take(self.expr_stack_mut()); + let mut stack = std::mem::take(self.expr_stack_mut()); tracing::trace!("stack: {stack:#?}"); let foc: FuncOrCtx = func_or_ctx.into(); @@ -605,7 +664,7 @@ pub trait Flatten: while (!ctx.is_ended(self).unwrap() || !ctx.live_edges(self).unwrap().is_empty()) && ctx.parse_idx(self) < stack.len() { - let res = self.interpret_step(arena, ctx, body_loc, &stack[..]); + let res = self.interpret_step(arena, ctx, body_loc, &mut stack); if unsafe { USE_DEBUG_SITE } { post_to_site(&*self, arena); } @@ -618,7 +677,7 @@ pub trait Flatten: arena: &mut RangeArena>, ctx: ContextNode, body_loc: Loc, - stack: &[FlatExpr], + stack: &mut Vec, ) -> Result<(), ExprErr> { self.flat_apply_to_edges( ctx, @@ -629,7 +688,9 @@ pub trait Flatten: arena: &mut RangeArena>, ctx: ContextNode, _: Loc, - stack: &[FlatExpr]| { analyzer.interpret_expr(arena, ctx, stack) }, + stack: &mut Vec| { + analyzer.interpret_expr(arena, ctx, stack) + }, ) } @@ -637,7 +698,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, ) -> Result<(), ExprErr> { use FlatExpr::*; @@ -646,13 +707,15 @@ pub trait Flatten: } let parse_idx = ctx.increment_parse_idx(self); - let kill_after_exec = parse_idx == stack.len().saturating_sub(1); let Some(next) = stack.get(parse_idx) else { - let loc = stack - .last() - .map(|l| l.try_loc()) - .flatten() - .unwrap_or(Loc::Implicit); + let mut loc = None; + let mut stack_rev_iter = stack.iter().rev(); + let mut loccer = stack_rev_iter.next(); + while loc.is_none() && loccer.is_some() { + loc = loccer.unwrap().try_loc(); + loccer = stack_rev_iter.next(); + } + let loc = loc.unwrap_or(Loc::Implicit); return ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc); }; let next = *next; @@ -664,10 +727,6 @@ pub trait Flatten: self.peek_expr_flag() ); - tracing::trace!( - "current expr_ret stack: {}", - ctx.debug_expr_stack_str(self).unwrap() - ); match next { // Flag expressions FunctionCallName(n, is_super, named_args) => { @@ -770,12 +829,26 @@ pub trait Flatten: List(_, _) => self.interp_list(ctx, stack, next, parse_idx), Parameter(_, _, _) => Ok(()), Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), - YulExpr(FlatYulExpr::YulStartBlock) => Ok(()), - YulExpr(FlatYulExpr::YulVariable(..)) => Ok(()), - YulExpr(FlatYulExpr::YulFunctionCallName(..)) => Ok(()), - YulExpr(FlatYulExpr::YulFuncCall(..)) => Ok(()), + YulExpr(FlatYulExpr::YulStartBlock) => { + self.increment_asm_block(); + Ok(()) + } + YulExpr(yul @ FlatYulExpr::YulVariable(..)) => self.interp_yul_var(arena, ctx, yul), + YulExpr(yul @ FlatYulExpr::YulFuncCall(..)) => { + self.interp_yul_func_call(arena, ctx, stack, yul) + } YulExpr(FlatYulExpr::YulSuffixAccess(..)) => Ok(()), - YulExpr(FlatYulExpr::YulEndBlock) => Ok(()), + YulExpr(yul @ FlatYulExpr::YulAssign(..)) => self.interp_yul_assign(arena, ctx, yul), + YulExpr(yul @ FlatYulExpr::YulFuncDef(..)) => { + self.interp_yul_func_def(arena, ctx, stack, yul, parse_idx) + } + YulExpr(yul @ FlatYulExpr::YulVarDecl(..)) => { + self.interp_yul_var_decl(arena, ctx, stack, yul, parse_idx) + } + YulExpr(FlatYulExpr::YulEndBlock) => { + self.decrement_asm_block(); + Ok(()) + } }?; if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) @@ -816,7 +889,7 @@ pub trait Flatten: fn interp_list( &mut self, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, list: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -898,7 +971,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, if_expr: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -1160,7 +1233,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, var: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -1259,7 +1332,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, func_call: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -1273,7 +1346,12 @@ pub trait Flatten: self.interp_func_call(arena, ctx, func_call, Some(names)) } - fn get_named_args(&self, stack: &[FlatExpr], start: usize, n: usize) -> Vec<&'static str> { + fn get_named_args( + &self, + stack: &mut Vec, + start: usize, + n: usize, + ) -> Vec<&'static str> { stack[start..start + n] .iter() .map(|named_arg| { @@ -1410,20 +1488,6 @@ pub trait Flatten: } } - fn interp_super_func_call( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - next: FlatExpr, - ) -> Result<(), ExprErr> { - let FlatExpr::Super(loc, name) = next else { - unreachable!() - }; - - // self.super_access(arena, ctx, name, loc) - todo!() - } - fn interp_negatable_literal( &mut self, arena: &mut RangeArena>, @@ -1451,6 +1515,164 @@ pub trait Flatten: } } + fn interp_yul_func_call( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + next: FlatYulExpr, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulFuncCall(loc, name, num_inputs) = next else { + unreachable!() + }; + let inputs = ExprRet::Multi( + ctx.pop_n_latest_exprs(num_inputs, loc, self) + .into_expr_err(loc)?, + ); + self.yul_func_call( + arena, + ctx, + stack, + name, + inputs, + self.current_asm_block(), + loc, + ) + } + + fn interp_yul_var( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatYulExpr, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulVariable(loc, name) = next else { + unreachable!() + }; + + self.variable( + arena, + &solang_parser::pt::Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + )?; + + if let Some(ret) = ctx.pop_expr_latest(loc, self).into_expr_err(loc)? { + if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) + .is_memory(self) + .into_expr_err(loc)? + { + // its a memory based variable, push a uint instead + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } else { + ctx.push_expr(ret, self).into_expr_err(loc) + } + } else { + Err(ExprErr::Unresolved( + loc, + format!("Could not find yul variable with name: {name}"), + )) + } + } + + fn interp_yul_func_def( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + next: FlatYulExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulFuncDef(loc, name, num) = next else { + unreachable!() + }; + + let end = parse_idx + 1 + num; + let exprs = (&stack[parse_idx + 1..end]).to_vec(); + let fn_node = ctx.associated_fn(self).into_expr_err(loc)?; + let yul_fn = self.add_node(YulFunction::new(exprs, name, loc)); + self.add_edge(yul_fn, fn_node, Edge::YulFunction(self.current_asm_block())); + ctx.underlying_mut(self).into_expr_err(loc)?.parse_idx = end; + Ok(()) + } + + fn interp_yul_assign( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatYulExpr, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulAssign(loc, num) = next else { + unreachable!() + }; + + let to_assign = ctx + .pop_n_latest_exprs(num * 2, loc, self) + .into_expr_err(loc)?; + let (to_assign_to, assignments) = to_assign.split_at(to_assign.len() / 2); + assert!(assignments.len() == to_assign_to.len()); + to_assign_to + .iter() + .zip(assignments) + .try_for_each(|(lhs, rhs)| self.match_assign_sides(arena, ctx, loc, lhs, rhs)) + } + + fn interp_yul_var_decl( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + next: FlatYulExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulVarDecl(loc, num, assign) = next else { + unreachable!() + }; + + if assign { + let names = stack[parse_idx - num * 2..parse_idx] + .iter() + .filter_map(|s| match s { + FlatExpr::VarDef(_, Some(name), _, _) => Some(name), + _ => None, + }) + .collect::>(); + + names.iter().try_for_each(|name| { + self.variable( + arena, + &Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + ) + })?; + let to_assign = ctx + .pop_n_latest_exprs(num * 2, loc, self) + .into_expr_err(loc)?; + let (to_assign_to, assignments) = to_assign.split_at(to_assign.len() / 2); + assert!(assignments.len() == to_assign_to.len()); + to_assign_to + .iter() + .zip(assignments) + .try_for_each(|(lhs, rhs)| self.match_assign_sides(arena, ctx, loc, lhs, rhs)) + } else { + Ok(()) + } + } + fn modify_edges( &mut self, ctx: ContextNode, @@ -1489,13 +1711,13 @@ pub trait Flatten: ctx: ContextNode, loc: Loc, arena: &mut RangeArena>, - stack: &[FlatExpr], + stack: &mut Vec, closure: &impl Fn( &mut Self, &mut RangeArena>, ContextNode, Loc, - &[FlatExpr], + &mut Vec, ) -> Result<(), ExprErr>, ) -> Result<(), ExprErr> { let live_edges = ctx.live_edges(self).into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 894531e2..1f204193 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -8,7 +8,7 @@ use graph::{ }, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, SolcRange, VarType, }; -use shared::{ExprErr, IntoExprErr, RangeArena, StorageLocation}; +use shared::{ExprErr, FlatExpr, IntoExprErr, RangeArena, StorageLocation}; use ethers_core::types::U256; use solang_parser::pt::{Expression, Loc, YulExpression, YulFunctionCall}; @@ -23,677 +23,508 @@ pub trait YulFuncCaller: fn yul_func_call( &mut self, arena: &mut RangeArena>, - func_call: &YulFunctionCall, ctx: ContextNode, + stack: &mut Vec, + name: &str, + inputs: ExprRet, + assembly_block_idx: usize, + loc: Loc, ) -> Result<(), ExprErr> { - unreachable!() - // let YulFunctionCall { loc, id, arguments } = func_call; - - // match &*id.name { - // "caller" => { - // let t = self.msg_access(*loc, ctx, "sender")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "origin" => { - // let t = self.msg_access(*loc, ctx, "origin")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "gasprice" => { - // let t = self.msg_access(*loc, ctx, "gasprice")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "callvalue" => { - // let t = self.msg_access(*loc, ctx, "value")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "pop" => { - // let _ = ctx.pop_expr_latest(*loc, self).into_expr_err(*loc)?; - // Ok(()) - // } - // "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" - // | "prevrandao" | "timestamp" => { - // let t = self.block_access(*loc, ctx, &id.name)?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "log0" | "log1" | "log2" | "log3" | "log4" => { - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "stop" | "revert" | "selfdestruct" | "invalid" => { - // ctx.kill(self, *loc, KilledKind::Revert).into_expr_err(*loc) - // } - // "return" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(offset) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs(loc, "Yul Return had no offset".to_string())); - // }; - // if matches!(offset, ExprRet::CtxKilled(_)) { - // ctx.push_expr(offset, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(size) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs(loc, "Yul Return had no size".to_string())); - // }; - // if matches!(size, ExprRet::CtxKilled(_)) { - // ctx.push_expr(size, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.return_yul(ctx, loc, size)?; - // ctx.kill(analyzer, loc, KilledKind::Ended) - // .into_expr_err(loc)?; - // // ctx.push_expr(ExprRet::CtxKilled(KilledKind::Ended), analyzer) - // // .into_expr_err(loc)?; - // Ok(()) - // }) - // }) - // } - // "not" => { - // if arguments.len() != 1 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `not` expected 1 argument found: {:?}", - // arguments.len() - // ), - // )); - // } - - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoRhs( - // loc, - // "Not operation had no element".to_string(), - // )); - // }; - - // if matches!(lhs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.bit_not_inner(arena, ctx, loc, lhs.flatten()) - // }) - // } - // "add" | "sub" | "mul" | "div" | "sdiv" | "mod" | "smod" | "exp" | "and" | "or" - // | "xor" | "shl" | "shr" | "sar" => { - // let op = match &*id.name { - // "add" => RangeOp::Add(true), - // "sub" => RangeOp::Sub(true), - // "mul" => RangeOp::Mul(true), - // "div" | "sdiv" => RangeOp::Div(true), - // "mod" | "smod" => RangeOp::Mod, - // "exp" => RangeOp::Exp(true), - // "and" => RangeOp::BitAnd, - // "or" => RangeOp::BitOr, - // "xor" => RangeOp::BitXor, - // "shl" => RangeOp::Shl, - // "shr" | "sar" => RangeOp::Shr, - // _ => unreachable!(), - // }; - - // if arguments.len() != 2 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 2 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } - - // let inputs: Vec = if matches!(&*id.name, "shl" | "shr" | "sar") { - // // yul shifts are super dumb and are reversed. - // vec![arguments[1].clone(), arguments[0].clone()] - // } else { - // vec![arguments[0].clone(), arguments[1].clone()] - // }; - - // inputs.expect_length(2).into_expr_err(loc)?; - // let inputs = inputs.as_vec(); - - // // we have to cast the inputs into an EVM word, which is effectively a u256. - // let word_ty = analyzer.builtin_or_add(Builtin::Uint(256)); - // let cast_ty = VarType::try_from_idx(analyzer, word_ty).unwrap(); - // let lhs_paths = - // ContextVarNode::from(inputs[0].expect_single().into_expr_err(loc)?); - // lhs_paths - // .cast_from_ty(cast_ty.clone(), analyzer, arena) - // .into_expr_err(loc)?; - - // let rhs_paths = - // ContextVarNode::from(inputs[1].expect_single().into_expr_err(loc)?); - // rhs_paths - // .cast_from_ty(cast_ty, analyzer, arena) - // .into_expr_err(loc)?; - - // analyzer.op_match( - // arena, - // ctx, - // loc, - // &ExprRet::Single(lhs_paths.latest_version(analyzer).into()), - // &ExprRet::Single(rhs_paths.latest_version(analyzer).into()), - // op, - // false, - // ) - // } - // "lt" | "gt" | "slt" | "sgt" | "eq" => { - // let op = match &*id.name { - // "lt" | "slt" => RangeOp::Lt, - // "gt" | "sgt" => RangeOp::Gt, - // "eq" => RangeOp::Eq, - // _ => unreachable!(), - // }; - - // if arguments.len() != 2 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 2 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } - - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul Binary operation had no right hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Yul Binary operation had no left hand side".to_string(), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; - // let Some(result) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Yul Binary operation had no return".to_string(), - // )); - // }; - - // let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); - // let next = analyzer.advance_var_in_ctx(res, loc, ctx)?; - // let expr = Elem::Expr(RangeExpr::new( - // Elem::from(res), - // RangeOp::Cast, - // Elem::from(Concrete::Uint(256, U256::zero())), - // )); - - // next.set_range_min(analyzer, arena, expr.clone()) - // .into_expr_err(loc)?; - // next.set_range_max(analyzer, arena, expr) - // .into_expr_err(loc)?; - // ctx.push_expr(ExprRet::Single(next.into()), analyzer) - // .into_expr_err(loc) - // }) - // }) - // } - // "iszero" => { - // if arguments.len() != 1 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `iszero` expects 1 arguments found: {:?}", - // arguments.len() - // ), - // )); - // } - - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `iszero` operation had no input".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } + match name { + "caller" => { + let t = self.msg_access(ctx, "sender", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "origin" => { + let t = self.msg_access(ctx, "origin", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "gasprice" => { + let t = self.msg_access(ctx, "gasprice", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "callvalue" => { + let t = self.msg_access(ctx, "value", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "pop" => Ok(()), + "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" + | "prevrandao" | "timestamp" => { + let t = self.block_access(ctx, name, loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "log0" | "log1" | "log2" | "log3" | "log4" => { + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "stop" | "revert" | "selfdestruct" | "invalid" => { + ctx.kill(self, loc, KilledKind::Revert).into_expr_err(loc) + } + "return" => { + let [offset, size] = inputs.into_sized(); + self.return_yul(ctx, size, loc)?; + ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc)?; + Ok(()) + } + "not" => { + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `not` expected 1 argument found: {:?}", + inputs.len() + ), + )); + } - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::from(U256::from(0)))), - // ); - // let tmp_true = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let rhs_paths = - // ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_true)).into()); + let [lhs] = inputs.into_sized(); + self.bit_not_inner(arena, ctx, lhs.flatten(), loc) + } + "add" | "sub" | "mul" | "div" | "sdiv" | "mod" | "smod" | "exp" | "and" | "or" + | "xor" | "shl" | "shr" | "sar" => { + let op = match name { + "add" => RangeOp::Add(true), + "sub" => RangeOp::Sub(true), + "mul" => RangeOp::Mul(true), + "div" | "sdiv" => RangeOp::Div(true), + "mod" | "smod" => RangeOp::Mod, + "exp" => RangeOp::Exp(true), + "and" => RangeOp::BitAnd, + "or" => RangeOp::BitOr, + "xor" => RangeOp::BitXor, + "shl" => RangeOp::Shl, + "shr" | "sar" => RangeOp::Shr, + _ => unreachable!(), + }; + + if inputs.len() != 2 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 2 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, RangeOp::Eq, &rhs_paths) - // }) - // } - // "addmod" | "mulmod" => { - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "msize" | "pc" | "mload" | "sload" | "gas" | "returndatasize" => { - // // TODO: actually handle this. @MemoryModel - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "calldatacopy" => { - // // TODO: actually handle this. @MemoryModel - // Ok(()) - // } - // "calldatasize" => { - // // TODO: actually handle this. @MemoryModel - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "calldataload" => { - // if arguments.len() != 1 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `calldataload` expects 1 arguments found: {:?}", - // arguments.len() - // ), - // )); - // } + let [lhs, rhs] = if matches!(name, "shl" | "shr" | "sar") { + // yul shifts are super dumb and are reversed. + let [rhs, lhs] = inputs.into_sized(); + [lhs, rhs] + } else { + let [lhs, rhs] = inputs.into_sized(); + [lhs, rhs] + }; + + // we have to cast the inputs into an EVM word, which is effectively a u256. + let word_ty = self.builtin_or_add(Builtin::Uint(256)); + let cast_ty = VarType::try_from_idx(self, word_ty).unwrap(); + let lhs_paths = ContextVarNode::from(lhs.expect_single().into_expr_err(loc)?); + lhs_paths + .cast_from_ty(cast_ty.clone(), self, arena) + .into_expr_err(loc)?; + + let rhs_paths = ContextVarNode::from(rhs.expect_single().into_expr_err(loc)?); + rhs_paths + .cast_from_ty(cast_ty, self, arena) + .into_expr_err(loc)?; + + self.op_match( + arena, + ctx, + loc, + &ExprRet::Single(lhs_paths.latest_version(self).into()), + &ExprRet::Single(rhs_paths.latest_version(self).into()), + op, + false, + ) + } + "lt" | "gt" | "slt" | "sgt" | "eq" => { + let op = match name { + "lt" | "slt" => RangeOp::Lt, + "gt" | "sgt" => RangeOp::Gt, + "eq" => RangeOp::Eq, + _ => unreachable!(), + }; + + if inputs.len() != 2 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 2 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `calldataload` operation had no input".to_string(), - // )); - // }; - // // TODO: check const version - // let b = Builtin::Uint(256); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "calldata[{}:{}+32]", - // elem.display_name(analyzer).into_expr_err(loc)?, - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // "keccak256" => { - // let b = Builtin::Bytes(32); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "call" | "delegatecall" | "callcode" | "staticcall" => { - // let b = Builtin::Uint(256); - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b.clone()).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = format!("{id}_success"); - // let mut range = SolcRange::try_from_builtin(&b).unwrap(); - // range.min = Elem::from(Concrete::from(U256::from(0))); - // range.max = Elem::from(Concrete::from(U256::from(1))); - // var.ty.set_range(range).into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "create" | "create2" => { - // let b = Builtin::Address; - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = format!("{id}_success"); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "returndatacopy" => { - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "byte" => { - // let b = Builtin::Uint(8); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "mstore" | "mstore8" => { - // // TODO: improve this. Right now we are extremely pessimistic and just say we know nothing about memory variables anymore. - // // We should check if the location is a reference to an existing var and update based on that - // // @MemoryModel - // let vars = ctx.local_vars(self).clone(); - // vars.into_iter().try_for_each(|(_name, var)| { - // // widen to any max range - // let latest_var = var.latest_version_or_inherited_in_ctx(ctx, self); - // if matches!( - // latest_var.underlying(self).into_expr_err(*loc)?.storage, - // Some(StorageLocation::Memory(_)) - // ) { - // let res = latest_var.ty(self).into_expr_err(*loc)?; - // if let Some(r) = res.default_range(self).unwrap() { - // let new_var = self.advance_var_in_ctx(latest_var, *loc, ctx).unwrap(); - // let res = new_var - // .set_range_min(self, arena, r.min) - // .into_expr_err(*loc); - // let _ = self.add_if_err(res); - // let res = new_var - // .set_range_max(self, arena, r.max) - // .into_expr_err(*loc); - // let _ = self.add_if_err(res); - // } - // } - // Ok(()) - // })?; - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "sstore" => { - // if arguments.len() != 2 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 2 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } + let [lhs_paths, rhs_paths] = inputs.into_sized(); + + self.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; + let Some(result) = ctx.pop_expr_latest(loc, self).into_expr_err(loc)? else { + return Err(ExprErr::NoLhs( + loc, + "Yul Binary operation had no return".to_string(), + )); + }; + + let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); + let next = self.advance_var_in_ctx(res, loc, ctx)?; + let expr = Elem::Expr(RangeExpr::new( + Elem::from(res), + RangeOp::Cast, + Elem::from(Concrete::Uint(256, U256::zero())), + )); + + next.set_range_min(self, arena, expr.clone()) + .into_expr_err(loc)?; + next.set_range_max(self, arena, expr).into_expr_err(loc)?; + ctx.push_expr(ExprRet::Single(next.into()), self) + .into_expr_err(loc) + } + "iszero" => { + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `iszero` expects 1 arguments found: {:?}", + inputs.len() + ), + )); + } - // self.parse_inputs(arena, ctx, *loc, arguments)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(mut lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::InvalidFunctionInput( - // loc, - // "Yul `sload` operation had no inputs".to_string(), - // )); - // }; + let [lhs_paths] = inputs.into_sized(); - // if lhs_paths.expect_length(2).into_expr_err(loc).is_err() { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `sload` operation had no rhs".to_string(), - // )); - // } - // let value = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); - // let slot = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); - // let cvar = ContextVarNode::from(slot.expect_single().unwrap()); + let cnode = ConcreteNode::from(self.add_node(Concrete::from(U256::from(0)))); + let tmp_true = ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) + .into_expr_err(loc)?; + let rhs_paths = + ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - // if let Some(slot) = cvar.slot_to_storage(analyzer) { - // analyzer.match_assign_sides( - // arena, - // ctx, - // loc, - // &ExprRet::Single(slot.into()), - // &value, - // )?; - // } else { - // // TODO: improve this. We now handle `slot` but should try to figure out storage layout - // let vars = ctx.local_vars(analyzer).clone(); - // vars.iter().try_for_each(|(_name, var)| { - // // widen to any max range - // let latest_var = var.latest_version(analyzer); - // if matches!( - // latest_var.underlying(analyzer).into_expr_err(loc)?.storage, - // Some(StorageLocation::Storage(_)) - // ) { - // let res = latest_var.ty(analyzer).into_expr_err(loc)?; - // if let Some(r) = res.default_range(analyzer).unwrap() { - // let new_var = - // analyzer.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); - // let res = new_var - // .set_range_min(analyzer, arena, r.min) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // let res = new_var - // .set_range_max(analyzer, arena, r.max) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // } - // } - // Ok(()) - // })?; - // } - // Ok(()) - // })?; - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "balance" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `balance` operation had no input".to_string(), - // )); - // }; + self.cmp_inner(arena, ctx, loc, &lhs_paths, RangeOp::Eq, &rhs_paths) + } + "addmod" | "mulmod" => { + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "msize" | "pc" | "mload" | "sload" | "gas" | "returndatasize" => { + // TODO: actually handle this. @MemoryModel + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "calldatacopy" => { + // TODO: actually handle this. @MemoryModel + Ok(()) + } + "calldatasize" => { + // TODO: actually handle this. @MemoryModel + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "calldataload" => { + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `calldataload` expects 1 arguments found: {:?}", + inputs.len() + ), + )); + } - // let b = Builtin::Uint(256); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "balance({})", - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // "selfbalance" => { - // let b = Builtin::Uint(256); - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = "selfbalance()".to_string(); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc) - // } - // "address" => { - // let b = Builtin::Address; - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = "address()".to_string(); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc) - // } - // "extcodesize" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `extcodesize` operation had no input".to_string(), - // )); - // }; + let [lhs_paths] = inputs.into_sized(); + // TODO: check const version + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = format!( + "calldata[{}:{}+32]", + elem.display_name(self).into_expr_err(loc)?, + elem.display_name(self).into_expr_err(loc)? + ); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "keccak256" => { + let b = Builtin::Bytes(32); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "call" | "delegatecall" | "callcode" | "staticcall" => { + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b.clone()).into(), self) + .into_expr_err(loc)?; + var.display_name = format!("{name}_success"); + let mut range = SolcRange::try_from_builtin(&b).unwrap(); + range.min = Elem::from(Concrete::from(U256::from(0))); + range.max = Elem::from(Concrete::from(U256::from(1))); + var.ty.set_range(range).into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "create" | "create2" => { + let b = Builtin::Address; + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = format!("{name}_success"); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "returndatacopy" => { + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "byte" => { + let b = Builtin::Uint(8); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "mstore" | "mstore8" => { + // TODO: improve this. Right now we are extremely pessimistic and just say we know nothing about memory variables anymore. + // We should check if the location is a reference to an existing var and update based on that + // @MemoryModel + let vars = ctx.local_vars(self).clone(); + vars.into_iter().try_for_each(|(_name, var)| { + // widen to any max range + let latest_var = var.latest_version_or_inherited_in_ctx(ctx, self); + if matches!( + latest_var.underlying(self).into_expr_err(loc)?.storage, + Some(StorageLocation::Memory(_)) + ) { + let res = latest_var.ty(self).into_expr_err(loc)?; + if let Some(r) = res.default_range(self).unwrap() { + let new_var = self.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); + let res = new_var.set_range_min(self, arena, r.min).into_expr_err(loc); + let _ = self.add_if_err(res); + let res = new_var.set_range_max(self, arena, r.max).into_expr_err(loc); + let _ = self.add_if_err(res); + } + } + Ok(()) + })?; + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "sstore" => { + if inputs.len() != 2 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 2 arguments found: {:?}", + name, + inputs.len() + ), + )); + } + let [value, slot] = inputs.into_sized(); + let cvar = ContextVarNode::from(slot.expect_single().unwrap()); + + if let Some(slot) = cvar.slot_to_storage(self) { + self.match_assign_sides( + arena, + ctx, + loc, + &ExprRet::Single(slot.into()), + &value, + )?; + } else { + // TODO: improve this. We now handle `slot` but should try to figure out storage layout + let vars = ctx.local_vars(self).clone(); + vars.iter().try_for_each(|(_name, var)| { + // widen to any max range + let latest_var = var.latest_version(self); + if matches!( + latest_var.underlying(self).into_expr_err(loc)?.storage, + Some(StorageLocation::Storage(_)) + ) { + let res = latest_var.ty(self).into_expr_err(loc)?; + if let Some(r) = res.default_range(self).unwrap() { + let new_var = + self.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); + let res = + new_var.set_range_min(self, arena, r.min).into_expr_err(loc); + let _ = self.add_if_err(res); + let res = + new_var.set_range_max(self, arena, r.max).into_expr_err(loc); + let _ = self.add_if_err(res); + } + } + Ok(()) + })?; + } + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "balance" => { + let [lhs_paths] = inputs.into_sized(); - // let b = Builtin::Uint(256); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "extcodesize({})", - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // "codesize" => { - // let b = Builtin::Uint(256); - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = "codesize()".to_string(); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc) - // } - // "codecopy" => { - // if arguments.len() != 3 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 3 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = + format!("balance({})", elem.display_name(self).into_expr_err(loc)?); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "selfbalance" => { + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = "selfbalance()".to_string(); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "address" => { + let b = Builtin::Address; + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = "address()".to_string(); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "extcodesize" => { + let [lhs_paths] = inputs.into_sized(); + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = format!( + "extcodesize({})", + elem.display_name(self).into_expr_err(loc)? + ); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "codesize" => { + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = "codesize()".to_string(); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "codecopy" => { + if inputs.len() != 3 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 3 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // self.parse_inputs(arena, ctx, *loc, arguments)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `codecopy` operation had no input".to_string(), - // )); - // }; - // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - // .into_expr_err(loc) - // }) - // } - // "extcodecopy" => { - // if arguments.len() != 4 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 4 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } + let [_, _, _, _] = inputs.into_sized(); + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc) + } + "extcodecopy" => { + if inputs.len() != 4 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 4 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // self.parse_inputs(arena, ctx, *loc, arguments)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `extcodecopy` operation had no input".to_string(), - // )); - // }; - // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - // .into_expr_err(loc) - // }) - // } - // "extcodehash" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `extcodesize` operation had no input".to_string(), - // )); - // }; + let [_, _, _, _] = inputs.into_sized(); + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc) + } + "extcodehash" => { + let [lhs_paths] = inputs.into_sized(); - // let b = Builtin::Bytes(32); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "extcodehash({})", - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // _ => Err(ExprErr::Todo( - // *loc, - // format!("Unhandled yul function: \"{}\"", id.name), - // )), - // } + let b = Builtin::Bytes(32); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = format!( + "extcodehash({})", + elem.display_name(self).into_expr_err(loc)? + ); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + _ => { + let assoc_fn = ctx.associated_fn(self).into_expr_err(loc)?; + let all_yul_fns = assoc_fn.yul_funcs(self, assembly_block_idx); + if let Some(yul_fn) = all_yul_fns + .iter() + .find(|yul_fn| yul_fn.name(self).unwrap() == name) + { + let exprs = yul_fn.exprs(self).unwrap().clone(); + let end = stack.split_off(ctx.parse_idx(self)); + stack.extend(exprs); + stack.extend(end); + let inputs = inputs.as_vec(); + inputs + .into_iter() + .try_for_each(|i| ctx.push_expr(i, self).into_expr_err(loc)) + } else { + Err(ExprErr::Todo( + loc, + format!("Unhandled yul function: \"{}\"", name), + )) + } + } + } } - fn return_yul(&mut self, ctx: ContextNode, loc: Loc, size: ExprRet) -> Result<(), ExprErr> { + fn return_yul(&mut self, ctx: ContextNode, size: ExprRet, loc: Loc) -> Result<(), ExprErr> { match size { ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), ExprRet::Single(size) | ExprRet::SingleLiteral(size) => { @@ -724,7 +555,7 @@ pub trait YulFuncCaller: ExprRet::Multi(sizes) => { sizes .into_iter() - .try_for_each(|size| self.return_yul(ctx, loc, size))?; + .try_for_each(|size| self.return_yul(ctx, size, loc))?; Ok(()) } ExprRet::Null => Ok(()),