| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210 |
- // SPDX-License-Identifier: Apache-2.0
- use super::statements::{statement, LoopScopes};
- use super::{
- constant_folding, dead_storage,
- expression::expression,
- reaching_definitions, strength_reduce,
- vartable::{Vars, Vartable},
- vector_to_slice, Options,
- };
- use crate::codegen::subexpression_elimination::common_sub_expression_elimination;
- use crate::codegen::{undefined_variable, Expression, LLVMName};
- use crate::sema::ast::{
- CallTy, Contract, ExternalCallAccounts, FunctionAttributes, Namespace, Parameter, RetrieveType,
- Statement, StringLocation, StructType, Type,
- };
- use crate::sema::{contracts::collect_base_args, diagnostics::Diagnostics, Recurse};
- use crate::{sema::ast, Target};
- use indexmap::IndexMap;
- use num_bigint::BigInt;
- use num_traits::One;
- use parse_display::Display;
- use solang_parser::pt::CodeLocation;
- use solang_parser::pt::Loc;
- use solang_parser::pt::{self, FunctionTy};
- use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
- use std::ops::AddAssign;
- use std::str;
- use std::sync::Arc;
- use std::{fmt, fmt::Write};
- // IndexMap <ArrayVariable res , res of temp variable>
- pub type ArrayLengthVars = IndexMap<usize, usize>;
- #[derive(Debug, Clone)]
- #[allow(clippy::large_enum_variant)]
- pub enum Instr {
- /// Set variable
- Set {
- loc: pt::Loc,
- res: usize,
- expr: Expression,
- },
- /// Call internal function, either static dispatch or dynamic dispatch
- Call {
- res: Vec<usize>,
- return_tys: Vec<Type>,
- call: InternalCallTy,
- args: Vec<Expression>,
- },
- /// Return
- Return { value: Vec<Expression> },
- /// Jump unconditionally
- Branch { block: usize },
- /// Jump conditionally
- BranchCond {
- cond: Expression,
- true_block: usize,
- false_block: usize,
- },
- /// Set array element in memory
- Store { dest: Expression, data: Expression },
- /// Abort execution
- AssertFailure { encoded_args: Option<Expression> },
- /// Print to log message
- Print { expr: Expression },
- /// Load storage (this is an instruction rather than an expression
- /// so that it can be moved around by the dead storage pass
- LoadStorage {
- res: usize,
- ty: Type,
- storage: Expression,
- storage_type: Option<pt::StorageType>,
- },
- /// Clear storage at slot for ty (might span multiple slots)
- ClearStorage { ty: Type, storage: Expression },
- /// Set storage value at slot
- SetStorage {
- ty: Type,
- value: Expression,
- storage: Expression,
- storage_type: Option<pt::StorageType>,
- },
- /// In storage slot, set the value at the offset
- SetStorageBytes {
- value: Expression,
- storage: Expression,
- offset: Expression,
- },
- /// Push an element onto an array in storage
- PushStorage {
- res: usize,
- ty: Type,
- value: Option<Expression>,
- storage: Expression,
- },
- /// Pop an element from an array in storage
- PopStorage {
- res: Option<usize>,
- ty: Type,
- storage: Expression,
- },
- /// Push element on memory array
- PushMemory {
- res: usize,
- ty: Type,
- array: usize,
- value: Box<Expression>,
- },
- /// Pop element from memory array. The push builtin returns a reference
- /// to the new element which is stored in res.
- PopMemory {
- res: usize,
- ty: Type,
- array: usize,
- loc: Loc,
- },
- /// Create contract and call constructor. If creating the contract fails,
- /// either store the result in success or abort success.
- Constructor {
- success: Option<usize>,
- res: usize,
- contract_no: usize,
- constructor_no: Option<usize>,
- encoded_args: Expression,
- value: Option<Expression>,
- gas: Expression,
- salt: Option<Expression>,
- address: Option<Expression>,
- seeds: Option<Expression>,
- accounts: ExternalCallAccounts<Expression>,
- loc: Loc,
- },
- /// Call external functions. If the call fails, set the success failure
- /// or abort if this is None
- ExternalCall {
- loc: Loc,
- success: Option<usize>,
- address: Option<Expression>,
- accounts: ExternalCallAccounts<Expression>,
- seeds: Option<Expression>,
- payload: Expression,
- value: Expression,
- gas: Expression,
- callty: CallTy,
- contract_function_no: Option<(usize, usize)>,
- flags: Option<Expression>,
- },
- /// Value transfer; either address.send() or address.transfer()
- ValueTransfer {
- success: Option<usize>,
- address: Expression,
- value: Expression,
- },
- /// Self destruct
- SelfDestruct { recipient: Expression },
- /// Emit event
- EmitEvent {
- event_no: usize,
- data: Expression,
- topics: Vec<Expression>,
- },
- /// Write Buffer
- WriteBuffer {
- buf: Expression,
- offset: Expression,
- value: Expression,
- },
- /// Copy bytes from source address to destination address
- MemCopy {
- source: Expression,
- destination: Expression,
- bytes: Expression,
- },
- Switch {
- cond: Expression,
- cases: Vec<(Expression, usize)>,
- default: usize,
- },
- /// Do nothing
- Nop,
- /// Return AbiEncoded data via an environment system call
- ReturnData {
- data: Expression,
- data_len: Expression,
- },
- /// Return a code at the end of a function
- ReturnCode { code: ReturnCode },
- /// For unimplemented code, e.g. unsupported yul builtins. This instruction should
- /// only occur for the evm target, for which no emit is implemented yet. Once evm emit
- /// is implemented and all yul builtins are supported, this instruction should
- /// be removed. We only have this so we can pass evm code through sema/codegen, which is used
- /// by the language server and the ethereum solidity tests.
- Unimplemented { reachable: bool },
- /// This instruction serves to track account accesses through 'tx.accounts.my_account'
- /// on Solana, and has no emit implementation. It is exchanged by the proper
- /// Expression::Subscript at solana_accounts/account_management.rs
- AccountAccess {
- loc: pt::Loc,
- var_no: usize,
- name: String,
- },
- }
- /// This struct defined the return codes that we send to the execution environment when we return
- /// from a function.
- #[derive(PartialEq, Eq, Hash, Clone, Debug, Display)]
- #[display(style = "title case")]
- pub enum ReturnCode {
- Success,
- FunctionSelectorInvalid,
- AbiEncodingInvalid,
- InvalidDataError,
- AccountDataTooSmall,
- InvalidProgramId,
- }
- impl Instr {
- pub fn recurse_expressions<T>(
- &self,
- cx: &mut T,
- f: fn(expr: &Expression, ctx: &mut T) -> bool,
- ) {
- match self {
- Instr::BranchCond { cond: expr, .. }
- | Instr::LoadStorage { storage: expr, .. }
- | Instr::ClearStorage { storage: expr, .. }
- | Instr::Print { expr }
- | Instr::AssertFailure {
- encoded_args: Some(expr),
- }
- | Instr::PopStorage { storage: expr, .. }
- | Instr::SelfDestruct { recipient: expr }
- | Instr::Set { expr, .. } => {
- expr.recurse(cx, f);
- }
- Instr::PushMemory { value: expr, .. } => {
- expr.recurse(cx, f);
- }
- Instr::SetStorage {
- value: item_1,
- storage: item_2,
- ..
- }
- | Instr::Store {
- dest: item_1,
- data: item_2,
- }
- | Instr::ReturnData {
- data: item_1,
- data_len: item_2,
- } => {
- item_1.recurse(cx, f);
- item_2.recurse(cx, f);
- }
- Instr::PushStorage { value, storage, .. } => {
- if let Some(value) = value {
- value.recurse(cx, f);
- }
- storage.recurse(cx, f);
- }
- Instr::SetStorageBytes {
- value,
- storage,
- offset,
- } => {
- value.recurse(cx, f);
- storage.recurse(cx, f);
- offset.recurse(cx, f);
- }
- Instr::Return { value: exprs } | Instr::Call { args: exprs, .. } => {
- for expr in exprs {
- expr.recurse(cx, f);
- }
- }
- Instr::Constructor {
- encoded_args,
- value,
- gas,
- salt,
- address,
- accounts,
- ..
- } => {
- encoded_args.recurse(cx, f);
- if let Some(expr) = value {
- expr.recurse(cx, f);
- }
- gas.recurse(cx, f);
- if let Some(expr) = salt {
- expr.recurse(cx, f);
- }
- if let Some(expr) = address {
- expr.recurse(cx, f);
- }
- if let ExternalCallAccounts::Present(expr) = accounts {
- expr.recurse(cx, f);
- }
- }
- Instr::ExternalCall {
- address,
- payload,
- value,
- gas,
- ..
- } => {
- if let Some(expr) = address {
- expr.recurse(cx, f);
- }
- payload.recurse(cx, f);
- value.recurse(cx, f);
- gas.recurse(cx, f);
- }
- Instr::ValueTransfer { address, value, .. } => {
- address.recurse(cx, f);
- value.recurse(cx, f);
- }
- Instr::EmitEvent { data, topics, .. } => {
- data.recurse(cx, f);
- for expr in topics {
- expr.recurse(cx, f);
- }
- }
- Instr::WriteBuffer { offset, value, .. } => {
- value.recurse(cx, f);
- offset.recurse(cx, f);
- }
- Instr::MemCopy {
- source: from,
- destination: to,
- bytes,
- } => {
- from.recurse(cx, f);
- to.recurse(cx, f);
- bytes.recurse(cx, f);
- }
- Instr::Switch { cond, cases, .. } => {
- cond.recurse(cx, f);
- for (case, _) in cases {
- case.recurse(cx, f);
- }
- }
- Instr::AssertFailure { encoded_args: None }
- | Instr::Nop
- | Instr::ReturnCode { .. }
- | Instr::Branch { .. }
- | Instr::AccountAccess { .. }
- | Instr::PopMemory { .. }
- | Instr::Unimplemented { .. } => {}
- }
- }
- }
- #[derive(Clone, Debug)]
- #[allow(clippy::large_enum_variant)]
- pub enum InternalCallTy {
- Static { cfg_no: usize },
- Dynamic(Expression),
- Builtin { ast_func_no: usize },
- HostFunction { name: String },
- }
- #[derive(Clone, PartialEq, Eq)]
- pub enum HashTy {
- Keccak256,
- Ripemd160,
- Sha256,
- Blake2_256,
- Blake2_128,
- }
- impl fmt::Display for HashTy {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- HashTy::Keccak256 => write!(f, "keccak256"),
- HashTy::Ripemd160 => write!(f, "ripemd160"),
- HashTy::Sha256 => write!(f, "sha256"),
- HashTy::Blake2_128 => write!(f, "blake2_128"),
- HashTy::Blake2_256 => write!(f, "blake2_256"),
- }
- }
- }
- #[derive(Debug, Clone, Default)]
- pub struct BasicBlock {
- pub phis: Option<BTreeSet<usize>>,
- pub name: String,
- pub instr: Vec<Instr>,
- pub defs: reaching_definitions::VarDefs,
- pub loop_reaching_variables: HashSet<usize>,
- pub transfers: Vec<Vec<reaching_definitions::Transfer>>,
- }
- #[derive(Debug, Clone)]
- pub struct ControlFlowGraph {
- pub name: String,
- pub function_no: ASTFunction,
- pub params: Arc<Vec<Parameter<Type>>>,
- pub returns: Arc<Vec<Parameter<Type>>>,
- pub vars: Vars,
- pub blocks: Vec<BasicBlock>,
- pub nonpayable: bool,
- pub public: bool,
- pub ty: pt::FunctionTy,
- pub selector: Vec<u8>,
- current: usize,
- // A mapping between the res of an array and the res of the temp var holding its length.
- pub array_lengths_temps: ArrayLengthVars,
- /// Is this a modifier dispatch for which function number?
- pub modifier: Option<usize>,
- }
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
- pub enum ASTFunction {
- SolidityFunction(usize),
- YulFunction(usize),
- None,
- }
- impl BasicBlock {
- /// Fetch the blocks that can be executed after the block passed as argument
- pub fn successors(&self) -> Vec<usize> {
- let mut out = Vec::new();
- // out cfg has edge as the last instruction in a block
- for (i, instr) in self.instr.iter().rev().enumerate() {
- match instr {
- Instr::Branch { block } => {
- assert_eq!(i, 0, "Branch is not last instruction in block");
- out.push(*block);
- }
- Instr::BranchCond {
- true_block,
- false_block,
- ..
- } => {
- assert_eq!(i, 0, "BranchCond is not last instruction in block");
- out.push(*true_block);
- out.push(*false_block);
- }
- Instr::Switch { default, cases, .. } => {
- assert_eq!(i, 0, "Switch is not last instruction in block");
- out.push(*default);
- for (_, goto) in cases {
- out.push(*goto);
- }
- }
- Instr::AssertFailure { .. }
- | Instr::SelfDestruct { .. }
- | Instr::ReturnCode { .. }
- | Instr::ReturnData { .. }
- | Instr::Return { .. }
- | Instr::Unimplemented { reachable: false } => {
- assert_eq!(i, 0, "instruction should be last in block");
- }
- _ => {
- assert_ne!(i, 0, "instruction should not be last in block");
- }
- }
- }
- out
- }
- }
- impl ControlFlowGraph {
- pub fn new(name: String, function_no: ASTFunction) -> Self {
- let mut cfg = ControlFlowGraph {
- name,
- function_no,
- params: Arc::new(Vec::new()),
- returns: Arc::new(Vec::new()),
- vars: IndexMap::new(),
- blocks: Vec::new(),
- nonpayable: false,
- public: false,
- ty: pt::FunctionTy::Function,
- selector: Vec::new(),
- current: 0,
- array_lengths_temps: IndexMap::new(),
- modifier: None,
- };
- cfg.new_basic_block("entry".to_string());
- cfg
- }
- /// Create an empty CFG which will be replaced later
- pub fn placeholder() -> Self {
- ControlFlowGraph {
- name: String::new(),
- function_no: ASTFunction::None,
- params: Arc::new(Vec::new()),
- returns: Arc::new(Vec::new()),
- vars: IndexMap::new(),
- blocks: Vec::new(),
- nonpayable: false,
- public: false,
- ty: pt::FunctionTy::Function,
- selector: Vec::new(),
- current: 0,
- array_lengths_temps: IndexMap::new(),
- modifier: None,
- }
- }
- /// Is this a placeholder
- pub fn is_placeholder(&self) -> bool {
- self.blocks.is_empty()
- }
- pub fn new_basic_block(&mut self, name: String) -> usize {
- let pos = self.blocks.len();
- self.blocks.push(BasicBlock {
- name,
- instr: Vec::new(),
- phis: None,
- transfers: Vec::new(),
- defs: IndexMap::new(),
- loop_reaching_variables: HashSet::new(),
- });
- pos
- }
- pub fn set_phis(&mut self, block: usize, phis: BTreeSet<usize>) {
- if !phis.is_empty() {
- self.blocks[block].phis = Some(phis);
- }
- }
- pub fn set_basic_block(&mut self, pos: usize) {
- self.current = pos;
- }
- /// Add an instruction to the CFG
- pub fn add(&mut self, vartab: &mut Vartable, ins: Instr) {
- if let Instr::Set { res, .. } = ins {
- vartab.set_dirty(res);
- }
- self.blocks[self.current].instr.push(ins);
- }
- /// Retrieve the basic block being processed
- pub fn current_block(&self) -> usize {
- self.current
- }
- /// Function to modify array length temp by inserting an add/sub instruction in the cfg right after a push/pop instruction.
- /// The operands of the add/sub instruction are the temp variable, and +/- 1.
- pub fn modify_temp_array_length(
- &mut self,
- loc: pt::Loc,
- minus: bool, // If the function is called from pushMemory or popMemory
- array_pos: usize, // The res of array that push/pop is performed on
- vartab: &mut Vartable,
- ) {
- // If not empty
- if self.array_lengths_temps.contains_key(&array_pos) {
- let to_add = self.array_lengths_temps[&array_pos];
- let add_expr = if minus {
- Expression::Subtract {
- loc,
- ty: Type::Uint(32),
- overflowing: true,
- left: Box::new(Expression::Variable {
- loc,
- ty: Type::Uint(32),
- var_no: to_add,
- }),
- right: Box::new(Expression::NumberLiteral {
- loc,
- ty: Type::Uint(32),
- value: BigInt::one(),
- }),
- }
- } else {
- Expression::Add {
- loc,
- ty: Type::Uint(32),
- overflowing: true,
- left: Box::new(Expression::Variable {
- loc,
- ty: Type::Uint(32),
- var_no: to_add,
- }),
- right: Box::new(Expression::NumberLiteral {
- loc,
- ty: Type::Uint(32),
- value: BigInt::one(),
- }),
- }
- };
- // Add instruction to the cfg
- self.add(
- vartab,
- Instr::Set {
- loc,
- res: to_add,
- expr: add_expr,
- },
- );
- }
- }
- pub fn expr_to_string(&self, contract: &Contract, ns: &Namespace, expr: &Expression) -> String {
- match expr {
- Expression::FunctionArg { arg_no, .. } => format!("(arg #{arg_no})"),
- Expression::BoolLiteral { value: false, .. } => "false".to_string(),
- Expression::BoolLiteral { value: true, .. } => "true".to_string(),
- Expression::BytesLiteral {
- ty: Type::String,
- value,
- ..
- } => {
- format!("{}", String::from_utf8_lossy(value))
- }
- Expression::BytesLiteral { value, .. } => format!("hex\"{}\"", hex::encode(value)),
- Expression::NumberLiteral {
- ty: ty @ Type::Address(_),
- value,
- ..
- } => {
- format!("{} {:#x}", ty.to_string(ns), value)
- }
- Expression::NumberLiteral { ty, value, .. } => {
- format!("{} {}", ty.to_string(ns), value)
- }
- Expression::RationalNumberLiteral { ty, rational, .. } => {
- format!("{} {}", ty.to_string(ns), rational)
- }
- Expression::StructLiteral { values, .. } => format!(
- "struct {{ {} }}",
- values
- .iter()
- .map(|e| self.expr_to_string(contract, ns, e))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Expression::ConstArrayLiteral {
- dimensions, values, ..
- } => format!(
- "constant {} [ {} ]",
- dimensions.iter().fold(String::new(), |mut output, d| {
- write!(output, "[{d}]").unwrap();
- output
- }),
- values
- .iter()
- .map(|e| self.expr_to_string(contract, ns, e))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Expression::ArrayLiteral {
- dimensions, values, ..
- } => format!(
- "{} [ {} ]",
- dimensions.iter().fold(String::new(), |mut output, d| {
- write!(output, "[{d}]").unwrap();
- output
- }),
- values
- .iter()
- .map(|e| self.expr_to_string(contract, ns, e))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Expression::Add {
- overflowing,
- left,
- right,
- ..
- } => format!(
- "({}{} + {})",
- if *overflowing { "overflowing " } else { "" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::Subtract {
- overflowing,
- left,
- right,
- ..
- } => format!(
- "({}{} - {})",
- if *overflowing { "overflowing " } else { "" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::BitwiseOr { left, right, .. } => format!(
- "({} | {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::BitwiseAnd { left, right, .. } => format!(
- "({} & {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::BitwiseXor { left, right, .. } => format!(
- "({} ^ {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::ShiftLeft { left, right, .. } => format!(
- "({} << {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::ShiftRight { left, right, .. } => format!(
- "({} >> {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::Multiply {
- overflowing,
- left,
- right,
- ..
- } => format!(
- "({}{} * {})",
- if *overflowing { "overflowing " } else { "" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::SignedDivide { left, right, .. } => format!(
- "(signed divide {} / {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right),
- ),
- Expression::UnsignedDivide { left, right, .. } => format!(
- "(unsigned divide {} / {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right),
- ),
- Expression::SignedModulo { left, right, .. } => format!(
- "(signed modulo {} % {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::UnsignedModulo { left, right, .. } => format!(
- "(unsigned modulo {} % {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::Power {
- overflowing,
- base,
- exp,
- ..
- } => format!(
- "({}{} ** {})",
- if *overflowing { "overflowing " } else { "" },
- self.expr_to_string(contract, ns, base),
- self.expr_to_string(contract, ns, exp)
- ),
- Expression::Variable { var_no, .. } => {
- if let Some(var) = self.vars.get(var_no) {
- format!("%{}", var.id.name)
- } else {
- panic!("error: non-existing variable {var_no} in CFG");
- }
- }
- Expression::Load { expr, .. } => {
- format!("(load {})", self.expr_to_string(contract, ns, expr))
- }
- Expression::ZeroExt { ty, expr, .. } => format!(
- "(zext {} {})",
- ty.to_string(ns),
- self.expr_to_string(contract, ns, expr)
- ),
- Expression::SignExt { ty, expr, .. } => format!(
- "(sext {} {})",
- ty.to_string(ns),
- self.expr_to_string(contract, ns, expr)
- ),
- Expression::Trunc { ty, expr, .. } => format!(
- "(trunc {} {})",
- ty.to_string(ns),
- self.expr_to_string(contract, ns, expr)
- ),
- Expression::More {
- signed,
- left,
- right,
- ..
- } => format!(
- "({} more {} > {})",
- if *signed { "signed" } else { "unsigned" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::Less {
- signed,
- left,
- right,
- ..
- } => format!(
- "({} less {} < {})",
- if *signed { "signed" } else { "unsigned" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::MoreEqual {
- signed,
- left,
- right,
- ..
- } => format!(
- "({} {} >= {})",
- if *signed { "signed" } else { "unsigned" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::LessEqual {
- signed,
- left,
- right,
- ..
- } => format!(
- "({} {} <= {})",
- if *signed { "signed" } else { "unsigned" },
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::Equal { left, right, .. } => format!(
- "({} == {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::NotEqual { left, right, .. } => format!(
- "({} != {})",
- self.expr_to_string(contract, ns, left),
- self.expr_to_string(contract, ns, right)
- ),
- Expression::Subscript {
- array_ty: ty,
- expr,
- index,
- ..
- } => format!(
- "(subscript {} {}[{}])",
- ty.to_string(ns),
- self.expr_to_string(contract, ns, expr),
- self.expr_to_string(contract, ns, index)
- ),
- Expression::StorageArrayLength { array, elem_ty, .. } => format!(
- "(storage array length {}[{}])",
- self.expr_to_string(contract, ns, array),
- elem_ty.to_string(ns),
- ),
- Expression::StructMember { expr, member, .. } => format!(
- "(struct {} field {})",
- self.expr_to_string(contract, ns, expr),
- member
- ),
- Expression::Not { expr, .. } => {
- format!("!{}", self.expr_to_string(contract, ns, expr))
- }
- Expression::BitwiseNot { expr, .. } => {
- format!("~{}", self.expr_to_string(contract, ns, expr))
- }
- Expression::Negate { expr, .. } => {
- format!("-{}", self.expr_to_string(contract, ns, expr))
- }
- Expression::Poison => "☠".to_string(),
- Expression::AllocDynamicBytes {
- ty,
- size,
- initializer: None,
- ..
- } => {
- let ty = if let Type::Slice(ty) = ty {
- format!("slice {}", ty.to_string(ns))
- } else {
- ty.to_string(ns)
- };
- format!(
- "(alloc {} len {})",
- ty,
- self.expr_to_string(contract, ns, size)
- )
- }
- Expression::AllocDynamicBytes {
- ty,
- size,
- initializer: Some(init),
- ..
- } => {
- let ty = if let Type::Slice(ty) = ty {
- format!("slice {}", ty.to_string(ns))
- } else {
- ty.to_string(ns)
- };
- format!(
- "(alloc {} {} {})",
- ty,
- self.expr_to_string(contract, ns, size),
- match str::from_utf8(init) {
- Ok(s) => format!("\"{}\"", s.escape_debug()),
- Err(_) => format!("hex\"{}\"", hex::encode(init)),
- }
- )
- }
- Expression::StringCompare { left, right, .. } => format!(
- "(strcmp ({}) ({}))",
- self.location_to_string(contract, ns, left),
- self.location_to_string(contract, ns, right)
- ),
- Expression::Keccak256 { exprs, .. } => format!(
- "(keccak256 {})",
- exprs
- .iter()
- .map(|e| self.expr_to_string(contract, ns, e))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Expression::InternalFunctionCfg { cfg_no, .. } => {
- format!("function {}", contract.cfg[*cfg_no].name)
- }
- Expression::ReturnData { .. } => "(external call return data)".to_string(),
- Expression::Cast { ty, expr, .. } => format!(
- "{}({})",
- ty.to_string(ns),
- self.expr_to_string(contract, ns, expr)
- ),
- Expression::BytesCast { ty, from, expr, .. } => format!(
- "{} from:{} ({})",
- ty.to_string(ns),
- from.to_string(ns),
- self.expr_to_string(contract, ns, expr)
- ),
- Expression::Builtin {
- kind: builtin,
- args,
- ..
- } => format!(
- "(builtin {:?} ({}))",
- builtin,
- args.iter()
- .map(|a| self.expr_to_string(contract, ns, a))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Expression::FormatString { args: fields, .. } => format!(
- "(format string {})",
- fields
- .iter()
- .map(|(spec, a)| format!("({} {})", spec, self.expr_to_string(contract, ns, a)))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Expression::Undefined { .. } => "undef".to_string(),
- Expression::AdvancePointer {
- pointer,
- bytes_offset,
- } => {
- format!(
- "(advance ptr: {}, by: {})",
- self.expr_to_string(contract, ns, pointer),
- self.expr_to_string(contract, ns, bytes_offset)
- )
- }
- Expression::GetRef { expr, .. } => {
- format!("(deref {})", self.expr_to_string(contract, ns, expr))
- }
- Expression::VectorData { pointer } => {
- format!("pointer pos {}", self.expr_to_string(contract, ns, pointer))
- }
- }
- }
- fn location_to_string(
- &self,
- contract: &Contract,
- ns: &Namespace,
- l: &StringLocation<Expression>,
- ) -> String {
- match l {
- StringLocation::RunTime(e) => self.expr_to_string(contract, ns, e),
- StringLocation::CompileTime(literal) => match str::from_utf8(literal) {
- Ok(s) => format!("\"{}\"", s.to_owned()),
- Err(_) => format!("hex\"{}\"", hex::encode(literal)),
- },
- }
- }
- pub fn instr_to_string(&self, contract: &Contract, ns: &Namespace, instr: &Instr) -> String {
- match instr {
- Instr::Return { value } => format!(
- "return {}",
- value
- .iter()
- .map(|expr| self.expr_to_string(contract, ns, expr))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Instr::Set { res, expr, .. } => format!(
- "ty:{} %{} = {}",
- self.vars[res].ty.to_string(ns),
- self.vars[res].id.name,
- self.expr_to_string(contract, ns, expr)
- ),
- Instr::Branch { block } => format!("branch block{block}"),
- Instr::BranchCond {
- cond,
- true_block,
- false_block,
- } => format!(
- "branchcond {}, block{}, block{}",
- self.expr_to_string(contract, ns, cond),
- true_block,
- false_block,
- ),
- Instr::LoadStorage { ty, res, storage, .. } => format!(
- "%{} = load storage slot({}) ty:{}",
- self.vars[res].id.name,
- self.expr_to_string(contract, ns, storage),
- ty.to_string(ns),
- ),
- Instr::ClearStorage { ty, storage } => format!(
- "clear storage slot({}) ty:{}",
- self.expr_to_string(contract, ns, storage),
- ty.to_string(ns),
- ),
- Instr::SetStorage { ty, value, storage, .. } => format!(
- "store storage slot({}) ty:{} = {}",
- self.expr_to_string(contract, ns, storage),
- ty.to_string(ns),
- self.expr_to_string(contract, ns, value),
- ),
- Instr::SetStorageBytes {
- value,
- storage,
- offset,
- } => format!(
- "set storage slot({}) offset:{} = {}",
- self.expr_to_string(contract, ns, storage),
- self.expr_to_string(contract, ns, offset),
- self.expr_to_string(contract, ns, value),
- ),
- Instr::PushStorage {
- res,
- ty,
- storage,
- value,
- } => {
- format!(
- "%{} = push storage ty:{} slot:{} = {}",
- self.vars[res].id.name,
- ty.to_string(ns),
- self.expr_to_string(contract, ns, storage),
- if let Some(value) = value {
- self.expr_to_string(contract, ns, value)
- } else {
- String::from("empty")
- }
- )
- }
- Instr::PopStorage {
- res: Some(res),
- ty,
- storage,
- } => {
- format!(
- "%{} = pop storage ty:{} slot({})",
- self.vars[res].id.name,
- ty.to_string(ns),
- self.expr_to_string(contract, ns, storage),
- )
- }
- Instr::PopStorage {
- res: None,
- ty,
- storage,
- } => {
- format!(
- "pop storage ty:{} slot({})",
- ty.to_string(ns),
- self.expr_to_string(contract, ns, storage),
- )
- }
- Instr::PushMemory {
- res,
- ty,
- array,
- value,
- } => format!(
- "%{}, %{} = push array ty:{} value:{}",
- self.vars[res].id.name,
- self.vars[array].id.name,
- ty.to_string(ns),
- self.expr_to_string(contract, ns, value),
- ),
- Instr::PopMemory { res, ty, array, loc:_ } => format!(
- "%{}, %{} = pop array ty:{}",
- self.vars[res].id.name,
- self.vars[array].id.name,
- ty.to_string(ns),
- ),
- Instr::AssertFailure { encoded_args: None } => "assert-failure".to_string(),
- Instr::AssertFailure { encoded_args: Some(expr) } => {
- format!("assert-failure: buffer: {}",
- self.expr_to_string(contract, ns, expr),
- )
- }
- Instr::Call {
- res,
- call: InternalCallTy::Builtin { ast_func_no },
- args,
- ..
- } => format!(
- "{} = call builtin {} {}",
- res.iter()
- .map(|local| format!("%{}", self.vars[local].id.name))
- .collect::<Vec<String>>()
- .join(", "),
- ns.functions[*ast_func_no].id,
- args.iter()
- .map(|expr| self.expr_to_string(contract, ns, expr))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Instr::Call {
- res,
- call: InternalCallTy::Static { cfg_no },
- args,
- ..
- } => format!(
- "{} = call {} {}",
- res.iter()
- .map(|local| format!("%{}", self.vars[local].id.name))
- .collect::<Vec<String>>()
- .join(", "),
- contract.cfg[*cfg_no].name,
- args.iter()
- .map(|expr| self.expr_to_string(contract, ns, expr))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Instr::Call {
- res,
- call: InternalCallTy::Dynamic(cfg),
- args,
- ..
- } => format!(
- "{} = call {} {}",
- res.iter()
- .map(|local| format!("%{}", self.vars[local].id.name))
- .collect::<Vec<String>>()
- .join(", "),
- self.expr_to_string(contract, ns, cfg),
- args.iter()
- .map(|expr| self.expr_to_string(contract, ns, expr))
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Instr::Call { res, call: InternalCallTy::HostFunction { name }, args, .. } => {
- format!("{} = call host function {} {}",
- res.iter()
- .map(|local| format!("%{}", self.vars[local].id.name))
- .collect::<Vec<String>>()
- .join(", "),
- name,
- args.iter()
- .map(|expr| self.expr_to_string(contract, ns, expr))
- .collect::<Vec<String>>()
- .join(", ")
- )
- }
- Instr::ExternalCall {
- success,
- address,
- payload,
- value,
- accounts,
- seeds,
- gas,
- callty,
- contract_function_no,
- flags, ..
- } => {
- format!(
- "{} = external call::{} address:{} payload:{} value:{} gas:{} accounts:{} seeds:{} contract|function:{} flags:{}",
- match success {
- Some(i) => format!("%{}", self.vars[i].id.name),
- None => "_".to_string(),
- },
- callty,
- if let Some(address) = address {
- self.expr_to_string(contract, ns, address)
- } else {
- String::new()
- },
- self.expr_to_string(contract, ns, payload),
- self.expr_to_string(contract, ns, value),
- self.expr_to_string(contract, ns, gas),
- if let ExternalCallAccounts::Present(accounts) = accounts {
- self.expr_to_string(contract, ns, accounts)
- } else {
- String::new()
- },
- if let Some(seeds) = seeds {
- self.expr_to_string(contract, ns, seeds)
- } else {
- String::new()
- },
- if let Some((contract_no, function_no)) = contract_function_no {
- format!("({contract_no}, {function_no})")
- } else {
- "_".to_string()
- },
- flags.as_ref().map(|e| self.expr_to_string(contract, ns, e)).unwrap_or_default()
- )
- }
- Instr::ValueTransfer {
- success,
- address,
- value,
- } => {
- format!(
- "{} = value transfer address:{} value:{}",
- match success {
- Some(i) => format!("%{}", self.vars[i].id.name),
- None => "_".to_string(),
- },
- self.expr_to_string(contract, ns, address),
- self.expr_to_string(contract, ns, value),
- )
- }
- Instr::Store { dest, data } => format!(
- "store {}, {}",
- self.expr_to_string(contract, ns, dest),
- self.expr_to_string(contract, ns, data),
- ),
- Instr::Print { expr } => format!("print {}", self.expr_to_string(contract, ns, expr)),
- Instr::Constructor {
- success,
- res,
- contract_no,
- encoded_args,
- gas,
- salt,
- value,
- address,seeds,
- accounts,
- constructor_no,
- loc:_
- } => format!(
- "%{}, {} = constructor(no: {}) salt:{} value:{} gas:{} address:{} seeds:{} {} encoded buffer: {} accounts: {}",
- self.vars[res].id.name,
- match success {
- Some(i) => format!("%{}", self.vars[i].id.name),
- None => "_".to_string(),
- },
- if let Some(no) = constructor_no {
- format!("{no}")
- } else {
- String::new()
- },
- match salt {
- Some(salt) => self.expr_to_string(contract, ns, salt),
- None => "".to_string(),
- },
- match value {
- Some(value) => self.expr_to_string(contract, ns, value),
- None => "".to_string(),
- },
- self.expr_to_string(contract, ns, gas),
- match address {
- Some(address) => self.expr_to_string(contract, ns, address),
- None => "".to_string(),
- },
- if let Some(seeds) = seeds {
- self.expr_to_string(contract, ns, seeds)
- } else {
- String::new()
- },
- ns.contracts[*contract_no].id,
- self.expr_to_string(contract, ns, encoded_args),
- if let ExternalCallAccounts::Present(accounts) = accounts {
- self.expr_to_string(contract, ns, accounts)
- } else {
- String::new()
- }
- ),
- Instr::SelfDestruct { recipient } => format!(
- "selfdestruct {}",
- self.expr_to_string(contract, ns, recipient)
- ),
- Instr::WriteBuffer { buf, offset, value } => format!(
- "writebuffer buffer:{} offset:{} value:{}",
- self.expr_to_string(contract, ns, buf),
- self.expr_to_string(contract, ns, offset),
- self.expr_to_string(contract, ns, value)
- ),
- Instr::EmitEvent {
- data,
- topics,
- event_no,
- ..
- } => format!(
- "emit event {} topics {} data {} ",
- ns.events[*event_no].symbol_name(ns),
- topics
- .iter()
- .map(|expr| self.expr_to_string(contract, ns, expr))
- .collect::<Vec<String>>()
- .join(", "),
- self.expr_to_string(contract, ns, data)
- ),
- Instr::Nop => String::from("nop"),
- Instr::MemCopy {
- source: from,
- destination: to,
- bytes,
- } => {
- format!(
- "memcpy src: {}, dest: {}, bytes_len: {}",
- self.expr_to_string(contract, ns, from),
- self.expr_to_string(contract, ns, to),
- self.expr_to_string(contract, ns, bytes)
- )
- }
- Instr::Switch {
- cond,
- cases,
- default,
- } => {
- let mut description =
- format!("switch {}:", self.expr_to_string(contract, ns, cond),);
- for item in cases {
- description.push_str(
- format!(
- "\n\t\tcase {}: goto block #{}",
- self.expr_to_string(contract, ns, &item.0),
- item.1
- )
- .as_str(),
- );
- }
- description.push_str(format!("\n\t\tdefault: goto block #{default}").as_str());
- description
- }
- Instr::ReturnData { data, data_len } => {
- format!(
- "return data {}, data length: {}",
- self.expr_to_string(contract, ns, data),
- self.expr_to_string(contract, ns, data_len)
- )
- }
- Instr::ReturnCode { code } => {
- format!("return code: {code}")
- }
- Instr::Unimplemented { .. } => {
- "unimplemented".into()
- }
- Instr::AccountAccess { .. } => {
- unreachable!("Instr::AccountAccess shall never be in the final CFG")
- }
- }
- }
- pub fn basic_block_to_string(&self, contract: &Contract, ns: &Namespace, pos: usize) -> String {
- let mut s = format!("block{}: # {}\n", pos, self.blocks[pos].name);
- if let Some(ref phis) = self.blocks[pos].phis {
- writeln!(
- s,
- "\t# phis: {}",
- phis.iter()
- .map(|p| -> &str { &self.vars[p].id.name })
- .collect::<Vec<&str>>()
- .join(",")
- )
- .unwrap();
- }
- let defs = &self.blocks[pos].defs;
- if !defs.is_empty() {
- writeln!(
- s,
- "\t# reaching:{}",
- defs.iter()
- .map(|(var_no, defs)| format!(
- " {}:[{}]",
- &self.vars[var_no].id.name,
- defs.keys()
- .map(|d| format!("{}:{}", d.block_no, d.instr_no))
- .collect::<Vec<String>>()
- .join(", ")
- ))
- .collect::<Vec<String>>()
- .join(", ")
- )
- .unwrap();
- }
- for ins in &self.blocks[pos].instr {
- writeln!(s, "\t{}", self.instr_to_string(contract, ns, ins)).unwrap();
- }
- s
- }
- pub fn to_string(&self, contract: &Contract, ns: &Namespace) -> String {
- let mut s = String::from("");
- for i in 0..self.blocks.len() {
- s.push_str(&self.basic_block_to_string(contract, ns, i));
- }
- s
- }
- }
- /// Checks whether there is a virtual fallback or receive function
- fn is_there_virtual_function(
- ns: &Namespace,
- contract_no: usize,
- function_no: Option<usize>,
- ) -> bool {
- let default_constructor = &ns.default_constructor(contract_no);
- let func = match function_no {
- Some(function_no) => &ns.functions[function_no],
- None => default_constructor,
- };
- // if the function is a fallback or receive, then don't bother with the overriden functions; they cannot be used
- if func.ty == pt::FunctionTy::Receive {
- // if there is a virtual receive function, and it's not this one, ignore it
- if let Some(receive) = ns.contracts[contract_no].virtual_functions.get("@receive") {
- let receive = receive.last().unwrap();
- if Some(*receive) != function_no {
- return true;
- }
- }
- }
- if func.ty == pt::FunctionTy::Fallback {
- // if there is a virtual fallback function, and it's not this one, ignore it
- if let Some(fallback) = ns.contracts[contract_no].virtual_functions.get("@fallback") {
- let fallback = fallback.last().unwrap();
- if Some(*fallback) != function_no {
- return true;
- }
- }
- }
- if func.ty == pt::FunctionTy::Modifier || !func.has_body {
- return true;
- }
- false
- }
- /// Generate the CFG for a function. If function_no is None, generate the implicit default
- /// constructor
- pub fn generate_cfg(
- contract_no: usize,
- function_no: Option<usize>,
- cfg_no: usize,
- all_cfgs: &mut Vec<ControlFlowGraph>,
- ns: &mut Namespace,
- opt: &Options,
- ) {
- if is_there_virtual_function(ns, contract_no, function_no) {
- return;
- }
- let mut cfg = function_cfg(contract_no, function_no, ns, opt);
- let ast_fn = function_no
- .map(ASTFunction::SolidityFunction)
- .unwrap_or(ASTFunction::None);
- optimize_and_check_cfg(&mut cfg, ns, ast_fn, opt);
- if let Some(func_no) = function_no {
- let func = &ns.functions[func_no];
- // if the function has any modifiers, generate the modifier chain
- if !func.modifiers.is_empty() {
- // only function can have modifiers
- assert_eq!(func.ty, pt::FunctionTy::Function);
- let public = cfg.public;
- let nonpayable = cfg.nonpayable;
- cfg.public = false;
- for chain_no in (0..func.modifiers.len()).rev() {
- let modifier_cfg_no = all_cfgs.len();
- all_cfgs.push(cfg);
- cfg = generate_modifier_dispatch(
- contract_no,
- func_no,
- modifier_cfg_no,
- chain_no,
- ns,
- opt,
- );
- optimize_and_check_cfg(&mut cfg, ns, ast_fn, opt);
- }
- cfg.public = public;
- cfg.nonpayable = nonpayable;
- cfg.selector = ns.functions[func_no].selector(ns, &contract_no);
- cfg.modifier = Some(func_no);
- }
- }
- all_cfgs[cfg_no] = cfg;
- }
- /// resolve modifier call
- fn resolve_modifier_call<'a>(
- call: &'a ast::Expression,
- contract: &Contract,
- ) -> (usize, &'a Vec<ast::Expression>) {
- if let ast::Expression::InternalFunctionCall { function, args, .. } = call {
- if let ast::Expression::InternalFunction {
- function_no,
- signature,
- ..
- } = function.as_ref()
- {
- // is it a virtual function call
- let function_no = if let Some(signature) = signature {
- contract.virtual_functions[signature]
- .last()
- .copied()
- .unwrap()
- } else {
- *function_no
- };
- return (function_no, args);
- }
- }
- panic!("modifier should resolve to internal call");
- }
- /// Detect undefined variables and run codegen optimizer passess
- pub fn optimize_and_check_cfg(
- cfg: &mut ControlFlowGraph,
- ns: &mut Namespace,
- func_no: ASTFunction,
- opt: &Options,
- ) {
- reaching_definitions::find(cfg);
- if func_no != ASTFunction::None {
- // If there are undefined variables, we raise an error and don't run optimizations
- if undefined_variable::find_undefined_variables(cfg, ns, func_no) {
- return;
- }
- }
- // constant folding generates diagnostics, so always run it. This means that the diagnostics
- // do not depend which passes are enabled. If the constant_folding is not enabled, run it
- // dry mode.
- constant_folding::constant_folding(cfg, !opt.constant_folding, ns);
- if opt.vector_to_slice {
- vector_to_slice::vector_to_slice(cfg, ns);
- }
- if opt.strength_reduce {
- strength_reduce::strength_reduce(cfg, ns);
- }
- if opt.dead_storage {
- dead_storage::dead_storage(cfg, ns);
- }
- // If the function is a default constructor, there is nothing to optimize.
- if opt.common_subexpression_elimination && func_no != ASTFunction::None {
- common_sub_expression_elimination(cfg, ns);
- }
- }
- /// Generate the CFG for a function. If function_no is None, generate the implicit default
- /// constructor
- fn function_cfg(
- contract_no: usize,
- function_no: Option<usize>,
- ns: &mut Namespace,
- opt: &Options,
- ) -> ControlFlowGraph {
- let mut vartab = match function_no {
- Some(function_no) => {
- Vartable::from_symbol_table(&ns.functions[function_no].symtable, ns.next_id)
- }
- None => Vartable::new(ns.next_id),
- };
- let mut loops = LoopScopes::new();
- let default_constructor = &ns.default_constructor(contract_no);
- let func = match function_no {
- Some(function_no) => &ns.functions[function_no],
- None => default_constructor,
- };
- // symbol name
- let contract_name = match func.contract_no {
- Some(base_contract_no) => format!(
- "{}::{}",
- ns.contracts[contract_no].id, ns.contracts[base_contract_no].id
- ),
- None => ns.contracts[contract_no].id.to_string(),
- };
- let name = match func.ty {
- pt::FunctionTy::Function => {
- format!("{}::function::{}", contract_name, func.llvm_symbol(ns))
- }
- // There can be multiple constructors on Polkadot, give them an unique name
- pt::FunctionTy::Constructor => {
- format!(
- "{}::constructor::{}",
- contract_name,
- hex::encode(func.selector(ns, &contract_no))
- )
- }
- _ => format!("{}::{}", contract_name, func.ty),
- };
- let mut cfg = ControlFlowGraph::new(
- name,
- if let Some(num) = function_no {
- ASTFunction::SolidityFunction(num)
- } else {
- ASTFunction::None
- },
- );
- cfg.params = func.params.clone();
- cfg.returns = func.returns.clone();
- cfg.selector = func.selector(ns, &contract_no);
- cfg.public = ns.function_externally_callable(contract_no, function_no);
- cfg.ty = func.ty;
- cfg.nonpayable = !func.is_payable();
- // populate the argument variables
- populate_arguments(func, &mut cfg, &mut vartab);
- // Hold your breath, this is the trickest part of the codegen ahead.
- // For each contract, the top-level constructor calls the base constructors. The base
- // constructors do not call their base constructors; everything is called from the top
- // level constructor. This is done because the arguments to base constructor are only
- // known the top level constructor, since the arguments can be specified elsewhere
- // on a constructor for a superior class
- if func.ty == pt::FunctionTy::Constructor && func.contract_no == Some(contract_no) {
- let mut all_base_args = BTreeMap::new();
- let mut diagnostics = Diagnostics::default();
- // Find all the resolved arguments for base contracts. These can be attached
- // to the contract, or the constructor. Contracts can have multiple constructors
- // so this needs to follow the correct constructors all the way
- collect_base_args(
- contract_no,
- function_no,
- &mut all_base_args,
- &mut diagnostics,
- ns,
- );
- // We shouldn't have problems. sema should have checked this
- assert!(diagnostics.is_empty());
- let order = ns.contract_bases(contract_no);
- let mut gen_base_args: HashMap<usize, (usize, Vec<Expression>)> = HashMap::new();
- for base_no in order.iter().rev() {
- if *base_no == contract_no {
- // we can't evaluate arguments to ourselves.
- continue;
- }
- if let Some(base_args) = all_base_args.get(base_no) {
- // There might be some temporary variables needed from the symbol table where
- // the constructor arguments were defined
- if let Some(defined_constructor_no) = base_args.defined_constructor_no {
- let func = &ns.functions[defined_constructor_no];
- vartab.add_symbol_table(&func.symtable);
- }
- // So we are evaluating the base arguments, from superior to inferior. The results
- // must be stored somewhere, for two reasons:
- // - The results must be stored by-value, so that variable value don't change
- // by later base arguments (e.g. x++)
- // - The results are also arguments to the next constructor arguments, so they
- // might be used again. Therefore we store the result in the vartable entry
- // for the argument; this means values are passed automatically to the next
- // constructor. We do need the symbol table for the called constructor, therefore
- // we have the following two lines which look a bit odd at first
- let func = &ns.functions[base_args.calling_constructor_no];
- vartab.add_symbol_table(&func.symtable);
- let args: Vec<Expression> = base_args
- .args
- .iter()
- .enumerate()
- .map(|(i, a)| {
- let expr =
- expression(a, &mut cfg, contract_no, Some(func), ns, &mut vartab, opt);
- if let Some(id) = &func.symtable.arguments[i] {
- let ty = expr.ty();
- let loc = expr.loc();
- cfg.add(
- &mut vartab,
- Instr::Set {
- loc: func.params[i].loc,
- res: *id,
- expr,
- },
- );
- Expression::Variable {
- loc,
- ty,
- var_no: *id,
- }
- } else {
- Expression::Poison
- }
- })
- .collect();
- gen_base_args.insert(*base_no, (base_args.calling_constructor_no, args));
- }
- }
- for base_no in order.iter() {
- if *base_no == contract_no {
- // we can't evaluate arguments to ourselves.
- continue;
- }
- if let Some((constructor_no, args)) = gen_base_args.remove(base_no) {
- let cfg_no = ns.contracts[contract_no].all_functions[&constructor_no];
- cfg.add(
- &mut vartab,
- Instr::Call {
- res: Vec::new(),
- return_tys: Vec::new(),
- call: InternalCallTy::Static { cfg_no },
- args,
- },
- );
- } else if let Some(constructor_no) = ns.contracts[*base_no].no_args_constructor(ns) {
- let cfg_no = ns.contracts[contract_no].all_functions[&constructor_no];
- cfg.add(
- &mut vartab,
- Instr::Call {
- res: Vec::new(),
- return_tys: Vec::new(),
- call: InternalCallTy::Static { cfg_no },
- args: Vec::new(),
- },
- );
- }
- }
- }
- // named returns should be populated
- populate_named_returns(func, ns, &mut cfg, &mut vartab);
- for stmt in &func.body {
- statement(
- stmt,
- func,
- &mut cfg,
- contract_no,
- ns,
- &mut vartab,
- &mut loops,
- None,
- None,
- opt,
- );
- if !stmt.reachable() {
- break;
- }
- }
- if func.body.last().map(Statement::reachable).unwrap_or(true) {
- let loc = match func.body.last() {
- Some(ins) => ins.loc(),
- None => pt::Loc::Codegen,
- };
- // add implicit return
- cfg.add(
- &mut vartab,
- Instr::Return {
- value: func
- .symtable
- .returns
- .iter()
- .map(|pos| Expression::Variable {
- loc,
- ty: func.symtable.vars[pos].ty.clone(),
- var_no: *pos,
- })
- .collect::<Vec<_>>(),
- },
- );
- }
- vartab.finalize(ns, &mut cfg);
- // walk cfg to check for use for before initialize
- cfg
- }
- /// Populate the arguments of a function
- pub(crate) fn populate_arguments<T: FunctionAttributes>(
- func: &T,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ) {
- for (i, arg) in func.get_symbol_table().arguments.iter().enumerate() {
- if let Some(pos) = arg {
- let var = &func.get_symbol_table().vars[pos];
- cfg.add(
- vartab,
- Instr::Set {
- loc: func.get_parameters()[i].loc,
- res: *pos,
- expr: Expression::FunctionArg {
- loc: var.id.loc,
- ty: var.ty.clone(),
- arg_no: i,
- },
- },
- );
- }
- }
- }
- /// Populate returns of functions that have named returns
- pub(crate) fn populate_named_returns<T: FunctionAttributes>(
- func: &T,
- ns: &Namespace,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ) {
- for (i, pos) in func.get_symbol_table().returns.iter().enumerate() {
- if let Some(name) = &func.get_returns()[i].id {
- if let Some(expr) = func.get_returns()[i].ty.default(ns) {
- cfg.add(
- vartab,
- Instr::Set {
- loc: name.loc,
- res: *pos,
- expr,
- },
- );
- }
- }
- }
- }
- /// Generate the CFG for a modifier on a function
- fn generate_modifier_dispatch(
- contract_no: usize,
- func_no: usize,
- cfg_no: usize,
- chain_no: usize,
- ns: &mut Namespace,
- opt: &Options,
- ) -> ControlFlowGraph {
- let (modifier_no, args) = resolve_modifier_call(
- &ns.functions[func_no].modifiers[chain_no],
- &ns.contracts[contract_no],
- );
- let func = &ns.functions[func_no];
- let modifier = &ns.functions[modifier_no];
- let name = format!(
- "{}::{}::{}::modifier{}::{}",
- &ns.contracts[contract_no].id,
- &ns.contracts[func.contract_no.unwrap()].id,
- func.llvm_symbol(ns),
- chain_no,
- modifier.llvm_symbol(ns)
- );
- let mut cfg = ControlFlowGraph::new(name, ASTFunction::None);
- cfg.params = func.params.clone();
- cfg.returns = func.returns.clone();
- let mut vartab = Vartable::from_symbol_table(&func.symtable, ns.next_id);
- vartab.add_symbol_table(&modifier.symtable);
- let mut loops = LoopScopes::new();
- // a modifier takes the same arguments as the function it is applied to. This way we can pass
- // the arguments to the function
- for (i, arg) in func.symtable.arguments.iter().enumerate() {
- if let Some(pos) = arg {
- let var = &func.symtable.vars[pos];
- cfg.add(
- &mut vartab,
- Instr::Set {
- loc: var.id.loc,
- res: *pos,
- expr: Expression::FunctionArg {
- loc: var.id.loc,
- ty: var.ty.clone(),
- arg_no: i,
- },
- },
- );
- }
- }
- // now set the modifier args
- for (i, arg) in modifier.symtable.arguments.iter().enumerate() {
- if let Some(pos) = arg {
- let expr = expression(
- &args[i],
- &mut cfg,
- contract_no,
- Some(func),
- ns,
- &mut vartab,
- opt,
- );
- cfg.add(
- &mut vartab,
- Instr::Set {
- loc: expr.loc(),
- res: *pos,
- expr,
- },
- );
- }
- }
- // modifiers do not have return values in their syntax, but the return values from the function
- // need to be passed on. So, we need to create some var
- let mut value = Vec::new();
- let mut return_tys = Vec::new();
- for (i, arg) in func.returns.iter().enumerate() {
- value.push(Expression::Variable {
- loc: arg.loc,
- ty: arg.ty.clone(),
- var_no: func.symtable.returns[i],
- });
- return_tys.push(arg.ty.clone());
- }
- let return_instr = Instr::Return { value };
- // create the instruction for the place holder
- let placeholder = Instr::Call {
- res: func.symtable.returns.clone(),
- call: InternalCallTy::Static { cfg_no },
- return_tys,
- args: func
- .params
- .iter()
- .enumerate()
- .map(|(i, p)| Expression::FunctionArg {
- loc: p.loc,
- ty: p.ty.clone(),
- arg_no: i,
- })
- .collect(),
- };
- for stmt in &modifier.body {
- statement(
- stmt,
- modifier,
- &mut cfg,
- contract_no,
- ns,
- &mut vartab,
- &mut loops,
- Some(&placeholder),
- Some(&return_instr),
- opt,
- );
- }
- if modifier
- .body
- .last()
- .map(Statement::reachable)
- .unwrap_or(true)
- {
- let loc = match func.body.last() {
- Some(ins) => ins.loc(),
- None => pt::Loc::Codegen,
- };
- // add implicit return
- cfg.add(
- &mut vartab,
- Instr::Return {
- value: func
- .symtable
- .returns
- .iter()
- .map(|pos| Expression::Variable {
- loc,
- ty: func.symtable.vars[pos].ty.clone(),
- var_no: *pos,
- })
- .collect::<Vec<_>>(),
- },
- );
- }
- vartab.finalize(ns, &mut cfg);
- cfg
- }
- impl Contract {
- /// Print the entire contract; storage initializers, constructors and functions and their CFGs
- pub fn print_cfg(&self, ns: &Namespace) -> String {
- let mut out = format!("#\n# Contract: {}\n#\n\n", self.id);
- for cfg in &self.cfg {
- if !cfg.is_placeholder() {
- writeln!(
- out,
- "\n# {} {} public:{} selector:{} nonpayable:{}",
- cfg.ty,
- cfg.name,
- cfg.public,
- hex::encode(&cfg.selector),
- cfg.nonpayable,
- )
- .unwrap();
- writeln!(
- out,
- "# params: {}",
- cfg.params
- .iter()
- .map(|p| {
- if p.id.is_some() {
- format!("{} {}", p.ty.to_string(ns), p.name_as_str())
- } else {
- p.ty.to_string(ns)
- }
- })
- .collect::<Vec<String>>()
- .join(",")
- )
- .unwrap();
- writeln!(
- out,
- "# returns: {}",
- cfg.returns
- .iter()
- .map(|p| {
- if p.id.is_some() {
- format!("{} {}", p.ty.to_string(ns), p.name_as_str())
- } else {
- p.ty.to_string(ns)
- }
- })
- .collect::<Vec<String>>()
- .join(",")
- )
- .unwrap();
- out += &cfg.to_string(self, ns);
- }
- }
- out
- }
- /// Get the storage slot for a variable, possibly from base contract
- pub fn get_storage_slot(
- &self,
- loc: pt::Loc,
- var_contract_no: usize,
- var_no: usize,
- ns: &Namespace,
- ty: Option<Type>,
- ) -> Expression {
- if let Some(layout) = self
- .layout
- .iter()
- .find(|l| l.contract_no == var_contract_no && l.var_no == var_no)
- {
- Expression::NumberLiteral {
- loc,
- ty: ty.unwrap_or_else(|| ns.storage_type()),
- value: layout.slot.clone(),
- }
- } else {
- panic!("get_storage_slot called on non-storage variable");
- }
- }
- }
- impl Namespace {
- /// Determine whether a function should be included in the dispatcher and metadata,
- /// taking inheritance into account.
- ///
- /// `function_no` is optional because default constructors require creating a CFG
- /// without any corresponding function definition.
- pub fn function_externally_callable(
- &self,
- contract_no: usize,
- function_no: Option<usize>,
- ) -> bool {
- let default_constructor = &self.default_constructor(contract_no);
- let func = function_no
- .map(|n| &self.functions[n])
- .unwrap_or(default_constructor);
- // If a function is virtual, and it is overriden, do not make it public;
- // Otherwise the runtime function dispatch will have two identical functions to dispatch to.
- if func.is_virtual
- && self.contracts[contract_no]
- .virtual_functions
- .get(&func.signature)
- .and_then(|v| v.last())
- != function_no.as_ref()
- {
- return false;
- }
- if let Some(base_contract_no) = func.contract_no {
- return !(self.contracts[base_contract_no].is_library()
- || func.is_constructor() && contract_no != base_contract_no)
- && func.is_public()
- && func.ty != FunctionTy::Modifier;
- }
- false
- }
- /// Type storage
- pub fn storage_type(&self) -> Type {
- if self.target == Target::Solana {
- Type::Uint(32)
- } else if self.target == Target::Soroban {
- Type::Uint(64)
- } else {
- Type::Uint(256)
- }
- }
- /// Return the value type
- pub fn value_type(&self) -> Type {
- Type::Uint(8 * self.value_length as u16)
- }
- /// Checks if struct contains only primitive types and returns its memory non-padded size
- pub fn calculate_struct_non_padded_size(&self, struct_type: &StructType) -> Option<BigInt> {
- let mut size = BigInt::from(0u8);
- for field in &struct_type.definition(self).fields {
- let ty = field.ty.clone().unwrap_user_type(self);
- if !ty.is_primitive() {
- // If a struct contains a non-primitive type, we cannot calculate its
- // size during compile time
- if let Type::Struct(struct_ty) = &field.ty {
- if let Some(struct_size) = self.calculate_struct_non_padded_size(struct_ty) {
- size.add_assign(struct_size);
- continue;
- }
- }
- return None;
- } else {
- size.add_assign(ty.memory_size_of(self));
- }
- }
- Some(size)
- }
- }
|