| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824 |
- use super::storage::{
- array_offset, array_pop, array_push, storage_slots_array_pop, storage_slots_array_push,
- };
- use super::Options;
- use super::{
- cfg::{ControlFlowGraph, Instr, InternalCallTy},
- vartable::Vartable,
- };
- use crate::codegen::unused_variable::should_remove_assignment;
- use crate::codegen::{Builtin, Expression};
- use crate::sema::ast;
- use crate::sema::ast::RetrieveType;
- use crate::sema::ast::{CallTy, FormatArg, Function, Namespace, Parameter, StringLocation, Type};
- use crate::sema::eval::{eval_const_number, eval_const_rational};
- use crate::sema::expression::{bigint_to_expression, ResolveTo};
- use crate::Target;
- use num_bigint::BigInt;
- use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
- use solang_parser::pt;
- use solang_parser::pt::CodeLocation;
- use std::ops::Mul;
- pub fn expression(
- expr: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- match expr {
- ast::Expression::StorageVariable(_, _, var_contract_no, var_no) => {
- // base storage variables should precede contract variables, not overlap
- ns.contracts[contract_no].get_storage_slot(*var_contract_no, *var_no, ns, None)
- }
- ast::Expression::StorageLoad(loc, ty, expr) => {
- let storage = expression(expr, cfg, contract_no, func, ns, vartab, opt);
- load_storage(loc, ty, storage, cfg, vartab)
- }
- ast::Expression::Add(loc, ty, unchecked, left, right) => add(
- loc,
- ty,
- unchecked,
- left,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- right,
- opt,
- ),
- ast::Expression::Subtract(loc, ty, unchecked, left, right) => substract(
- loc,
- ty,
- unchecked,
- left,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- right,
- opt,
- ),
- ast::Expression::Multiply(loc, ty, unchecked, left, right) => {
- if ty.is_rational() {
- let (_, r) = eval_const_rational(expr, ns).unwrap();
- Expression::NumberLiteral(*loc, ty.clone(), r.to_integer())
- } else {
- Expression::Multiply(
- *loc,
- ty.clone(),
- *unchecked,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- )
- }
- }
- ast::Expression::Divide(loc, ty, left, right) => {
- let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
- let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
- if ty.is_signed_int() {
- Expression::SignedDivide(*loc, ty.clone(), Box::new(l), Box::new(r))
- } else {
- Expression::UnsignedDivide(*loc, ty.clone(), Box::new(l), Box::new(r))
- }
- }
- ast::Expression::Modulo(loc, ty, left, right) => {
- let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
- let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
- if ty.is_signed_int() {
- Expression::SignedModulo(*loc, ty.clone(), Box::new(l), Box::new(r))
- } else {
- Expression::UnsignedModulo(*loc, ty.clone(), Box::new(l), Box::new(r))
- }
- }
- ast::Expression::Power(loc, ty, unchecked, left, right) => Expression::Power(
- *loc,
- ty.clone(),
- *unchecked,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::BitwiseOr(loc, ty, left, right) => Expression::BitwiseOr(
- *loc,
- ty.clone(),
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::BitwiseAnd(loc, ty, left, right) => Expression::BitwiseAnd(
- *loc,
- ty.clone(),
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::BitwiseXor(loc, ty, left, right) => Expression::BitwiseXor(
- *loc,
- ty.clone(),
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::ShiftLeft(loc, ty, left, right) => Expression::ShiftLeft(
- *loc,
- ty.clone(),
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::ShiftRight(loc, ty, left, right, sign) => Expression::ShiftRight(
- *loc,
- ty.clone(),
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- *sign,
- ),
- ast::Expression::Equal(loc, left, right) => Expression::Equal(
- *loc,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::NotEqual(loc, left, right) => Expression::NotEqual(
- *loc,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::More(loc, left, right) => {
- let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
- let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
- if l.ty().is_signed_int() {
- Expression::SignedMore(*loc, Box::new(l), Box::new(r))
- } else {
- Expression::UnsignedMore(*loc, Box::new(l), Box::new(r))
- }
- }
- ast::Expression::MoreEqual(loc, left, right) => Expression::MoreEqual(
- *loc,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::Less(loc, left, right) => {
- let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
- let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
- if l.ty().is_signed_int() {
- Expression::SignedLess(*loc, Box::new(l), Box::new(r))
- } else {
- Expression::UnsignedLess(*loc, Box::new(l), Box::new(r))
- }
- }
- ast::Expression::LessEqual(loc, left, right) => Expression::LessEqual(
- *loc,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::ConstantVariable(_, _, Some(var_contract_no), var_no) => expression(
- ns.contracts[*var_contract_no].variables[*var_no]
- .initializer
- .as_ref()
- .unwrap(),
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- ),
- ast::Expression::ConstantVariable(_, _, None, var_no) => expression(
- ns.constants[*var_no].initializer.as_ref().unwrap(),
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- ),
- ast::Expression::Not(loc, expr) => Expression::Not(
- *loc,
- Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::Complement(loc, ty, expr) => Expression::Complement(
- *loc,
- ty.clone(),
- Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::UnaryMinus(loc, ty, expr) => Expression::UnaryMinus(
- *loc,
- ty.clone(),
- Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::StructLiteral(loc, ty, exprs) => Expression::StructLiteral(
- *loc,
- ty.clone(),
- exprs
- .iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect(),
- ),
- ast::Expression::ArrayLiteral(loc, ty, lengths, args) => Expression::ArrayLiteral(
- *loc,
- ty.clone(),
- lengths.clone(),
- args.iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect(),
- ),
- ast::Expression::ConstArrayLiteral(loc, ty, lengths, args) => {
- Expression::ConstArrayLiteral(
- *loc,
- ty.clone(),
- lengths.clone(),
- args.iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect(),
- )
- }
- ast::Expression::Assign(_, _, left, right) => {
- // If we reach this condition, the assignment is inside an expression.
- if let Some(function) = func {
- if should_remove_assignment(ns, left, function, opt) {
- return expression(right, cfg, contract_no, func, ns, vartab, opt);
- }
- }
- let cfg_right = expression(right, cfg, contract_no, func, ns, vartab, opt);
- assign_single(left, cfg_right, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::PreDecrement(loc, ty, unchecked, var)
- | ast::Expression::PreIncrement(loc, ty, unchecked, var) => pre_incdec(
- vartab,
- ty,
- var,
- cfg,
- contract_no,
- func,
- ns,
- loc,
- expr,
- unchecked,
- opt,
- ),
- ast::Expression::PostDecrement(loc, ty, unchecked, var)
- | ast::Expression::PostIncrement(loc, ty, unchecked, var) => post_incdec(
- vartab,
- ty,
- var,
- cfg,
- contract_no,
- func,
- ns,
- loc,
- expr,
- unchecked,
- opt,
- ),
- ast::Expression::Constructor {
- loc,
- contract_no,
- constructor_no,
- args,
- call_args,
- } => {
- let address_res = vartab.temp_anonymous(&Type::Contract(*contract_no));
- let args = args
- .iter()
- .map(|v| expression(v, cfg, *contract_no, func, ns, vartab, opt))
- .collect();
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, *contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let value = call_args
- .value
- .as_ref()
- .map(|value| expression(value, cfg, *contract_no, func, ns, vartab, opt));
- let salt = call_args
- .salt
- .as_ref()
- .map(|salt| expression(salt, cfg, *contract_no, func, ns, vartab, opt));
- let space = call_args
- .space
- .as_ref()
- .map(|space| expression(space, cfg, *contract_no, func, ns, vartab, opt));
- cfg.add(
- vartab,
- Instr::Constructor {
- success: None,
- res: address_res,
- contract_no: *contract_no,
- constructor_no: *constructor_no,
- args,
- value,
- gas,
- salt,
- space,
- },
- );
- Expression::Variable(*loc, Type::Contract(*contract_no), address_res)
- }
- ast::Expression::InternalFunction {
- function_no,
- signature,
- ..
- } => {
- let function_no = if let Some(signature) = signature {
- &ns.contracts[contract_no].virtual_functions[signature]
- } else {
- function_no
- };
- Expression::InternalFunctionCfg(ns.contracts[contract_no].all_functions[function_no])
- }
- ast::Expression::StorageArrayLength {
- loc,
- ty,
- array,
- elem_ty,
- } => {
- let array_ty = array.ty().deref_into();
- let array = expression(array, cfg, contract_no, func, ns, vartab, opt);
- match array_ty {
- Type::Bytes(length) => {
- let ast_expr = bigint_to_expression(
- loc,
- &BigInt::from_u8(length).unwrap(),
- ns,
- &mut Vec::new(),
- ResolveTo::Type(ty),
- )
- .unwrap();
- expression(&ast_expr, cfg, contract_no, func, ns, vartab, opt)
- }
- Type::DynamicBytes => Expression::StorageArrayLength {
- loc: *loc,
- ty: ty.clone(),
- array: Box::new(array),
- elem_ty: elem_ty.clone(),
- },
- Type::Array(_, dim) => match dim.last().unwrap() {
- None => {
- if ns.target == Target::Solana {
- Expression::StorageArrayLength {
- loc: *loc,
- ty: ty.clone(),
- array: Box::new(array),
- elem_ty: elem_ty.clone(),
- }
- } else {
- load_storage(loc, &ns.storage_type(), array, cfg, vartab)
- }
- }
- Some(length) => {
- let ast_expr = bigint_to_expression(
- loc,
- length,
- ns,
- &mut Vec::new(),
- ResolveTo::Type(ty),
- )
- .unwrap();
- expression(&ast_expr, cfg, contract_no, func, ns, vartab, opt)
- }
- },
- _ => unreachable!(),
- }
- }
- ast::Expression::Builtin(
- loc,
- returns,
- ast::Builtin::ExternalFunctionAddress,
- func_expr,
- ) => {
- if let ast::Expression::ExternalFunction { address, .. } = &func_expr[0] {
- expression(address, cfg, contract_no, func, ns, vartab, opt)
- } else {
- let func_expr = expression(&func_expr[0], cfg, contract_no, func, ns, vartab, opt);
- Expression::Builtin(
- *loc,
- returns.clone(),
- Builtin::ExternalFunctionAddress,
- vec![func_expr],
- )
- }
- }
- ast::Expression::Builtin(loc, returns, ast::Builtin::FunctionSelector, func_expr) => {
- match &func_expr[0] {
- ast::Expression::ExternalFunction { function_no, .. }
- | ast::Expression::InternalFunction { function_no, .. } => {
- let selector = ns.functions[*function_no].selector();
- Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::from(selector))
- }
- _ => {
- let func_expr =
- expression(&func_expr[0], cfg, contract_no, func, ns, vartab, opt);
- Expression::Builtin(
- *loc,
- returns.clone(),
- Builtin::FunctionSelector,
- vec![func_expr],
- )
- }
- }
- }
- ast::Expression::InternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCallRaw { .. }
- | ast::Expression::Builtin(_, _, ast::Builtin::AbiDecode, _) => {
- let mut returns = emit_function_call(expr, contract_no, cfg, func, ns, vartab, opt);
- returns.remove(0)
- }
- ast::Expression::ExternalFunction {
- loc,
- ty,
- address,
- function_no,
- } => {
- let address = expression(address, cfg, contract_no, func, ns, vartab, opt);
- Expression::ExternalFunction {
- loc: *loc,
- ty: ty.clone(),
- address: Box::new(address),
- function_no: *function_no,
- }
- }
- ast::Expression::Subscript(loc, elem_ty, array_ty, array, index) => array_subscript(
- loc,
- elem_ty,
- array_ty,
- array,
- index,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- ),
- ast::Expression::StructMember(loc, ty, var, field_no) if ty.is_contract_storage() => {
- if let Type::Struct(struct_no) = var.ty().deref_any() {
- let offset = if ns.target == Target::Solana {
- ns.structs[*struct_no].storage_offsets[*field_no].clone()
- } else {
- ns.structs[*struct_no].fields[..*field_no]
- .iter()
- .map(|field| field.ty.storage_slots(ns))
- .sum()
- };
- Expression::Add(
- *loc,
- ty.clone(),
- true,
- Box::new(expression(var, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(Expression::NumberLiteral(*loc, ns.storage_type(), offset)),
- )
- } else {
- unreachable!();
- }
- }
- ast::Expression::StructMember(loc, ty, var, member) => Expression::StructMember(
- *loc,
- ty.clone(),
- Box::new(expression(var, cfg, contract_no, func, ns, vartab, opt)),
- *member,
- ),
- ast::Expression::StringCompare(loc, left, right) => Expression::StringCompare(
- *loc,
- string_location(left, cfg, contract_no, func, ns, vartab, opt),
- string_location(right, cfg, contract_no, func, ns, vartab, opt),
- ),
- ast::Expression::StringConcat(loc, ty, left, right) => Expression::StringConcat(
- *loc,
- ty.clone(),
- string_location(left, cfg, contract_no, func, ns, vartab, opt),
- string_location(right, cfg, contract_no, func, ns, vartab, opt),
- ),
- ast::Expression::Or(loc, left, right) => {
- expr_or(left, cfg, contract_no, func, ns, vartab, loc, right, opt)
- }
- ast::Expression::And(loc, left, right) => {
- and(left, cfg, contract_no, func, ns, vartab, loc, right, opt)
- }
- ast::Expression::CheckingTrunc(loc, ty, e) => {
- checking_trunc(loc, e, ty, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Trunc(loc, ty, e) => Expression::Trunc(
- *loc,
- ty.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::ZeroExt(loc, ty, e) => Expression::ZeroExt(
- *loc,
- ty.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::SignExt(loc, ty, e) => Expression::SignExt(
- *loc,
- ty.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::Cast(loc, ty @ Type::Address(_), e) => {
- if let Ok((_, address)) = eval_const_number(e, ns) {
- Expression::NumberLiteral(*loc, ty.clone(), address)
- } else {
- Expression::Cast(
- *loc,
- ty.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- )
- }
- }
- ast::Expression::Cast(loc, ty, e) => {
- if e.ty() == Type::Rational {
- let (_, n) = eval_const_rational(e, ns).unwrap();
- Expression::NumberLiteral(*loc, ty.clone(), n.to_integer())
- } else if matches!(ty, Type::String | Type::DynamicBytes)
- && matches!(expr.ty(), Type::String | Type::DynamicBytes)
- {
- expression(e, cfg, contract_no, func, ns, vartab, opt)
- } else {
- Expression::Cast(
- *loc,
- ty.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- )
- }
- }
- ast::Expression::BytesCast(loc, ty, from, e) => Expression::BytesCast(
- *loc,
- ty.clone(),
- from.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- ),
- ast::Expression::Load(loc, ty, e) => Expression::Load(
- *loc,
- ty.clone(),
- Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
- ),
- // for some built-ins, we have to inline special case code
- ast::Expression::Builtin(_, _, ast::Builtin::UserTypeWrap, args)
- | ast::Expression::Builtin(_, _, ast::Builtin::UserTypeUnwrap, args) => {
- expression(&args[0], cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(loc, ty, ast::Builtin::ArrayPush, args) => {
- if args[0].ty().is_contract_storage() {
- if ns.target == Target::Solana || args[0].ty().is_storage_bytes() {
- array_push(loc, args, cfg, contract_no, func, ns, vartab, opt)
- } else {
- storage_slots_array_push(loc, args, cfg, contract_no, func, ns, vartab, opt)
- }
- } else {
- let second_arg = if args.len() > 1 {
- expression(&args[1], cfg, contract_no, func, ns, vartab, opt)
- } else {
- ty[0].default(ns).unwrap()
- };
- memory_array_push(
- &ty[0],
- vartab,
- &args[0],
- cfg,
- contract_no,
- func,
- ns,
- second_arg,
- loc,
- opt,
- )
- }
- }
- ast::Expression::Builtin(loc, ty, ast::Builtin::ArrayPop, args) => {
- if args[0].ty().is_contract_storage() {
- if ns.target == Target::Solana || args[0].ty().is_storage_bytes() {
- array_pop(loc, args, &ty[0], cfg, contract_no, func, ns, vartab, opt)
- } else {
- storage_slots_array_pop(
- loc,
- args,
- &ty[0],
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- )
- }
- } else {
- let address_res = vartab.temp_anonymous(&ty[0]);
- let address_arr =
- match expression(&args[0], cfg, contract_no, func, ns, vartab, opt) {
- Expression::Variable(_, _, pos) => pos,
- _ => unreachable!(),
- };
- cfg.add(
- vartab,
- Instr::PopMemory {
- res: address_res,
- ty: args[0].ty(),
- array: address_arr,
- },
- );
- Expression::Variable(*loc, ty[0].clone(), address_res)
- }
- }
- ast::Expression::Builtin(_, _, ast::Builtin::Assert, args) => {
- expr_assert(cfg, &args[0], contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(_, _, ast::Builtin::Print, args) => {
- let expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- cfg.add(vartab, Instr::Print { expr });
- Expression::Poison
- }
- ast::Expression::Builtin(_, _, ast::Builtin::Require, args) => {
- require(cfg, args, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(_, _, ast::Builtin::Revert, args) => {
- revert(args, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(_, _, ast::Builtin::SelfDestruct, args) => {
- self_destruct(args, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::PayableSend, args) => {
- payable_send(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::PayableTransfer, args) => {
- payable_transfer(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncode, args) => {
- abi_encode(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodePacked, args) => {
- abi_encode_packed(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodeWithSelector, args) => {
- abi_encode_with_selector(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodeWithSignature, args) => {
- abi_encode_with_signature(args, loc, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodeCall, args) => {
- abi_encode_call(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- // The Substrate gas price builtin takes an argument; the others do not
- ast::Expression::Builtin(loc, _, ast::Builtin::Gasprice, expr)
- if expr.len() == 1 && ns.target == Target::Ewasm =>
- {
- builtin_ewasm_gasprice(loc, expr, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin(loc, tys, builtin, args) => expr_builtin(
- args,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- loc,
- tys,
- builtin,
- opt,
- ),
- ast::Expression::FormatString(loc, args) => {
- format_string(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::AllocDynamicArray(loc, ty, size, init) => {
- alloc_dynamic_array(size, cfg, contract_no, func, ns, vartab, loc, ty, init, opt)
- }
- ast::Expression::Ternary(loc, ty, cond, left, right) => ternary(
- loc,
- ty,
- cond,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- left,
- right,
- opt,
- ),
- ast::Expression::InterfaceId(loc, contract_no) => interfaceid(ns, contract_no, loc),
- ast::Expression::BoolLiteral(loc, value) => Expression::BoolLiteral(*loc, *value),
- ast::Expression::BytesLiteral(loc, ty, arr) => {
- Expression::BytesLiteral(*loc, ty.clone(), arr.clone())
- }
- ast::Expression::CodeLiteral(loc, pos, boolean) => {
- Expression::CodeLiteral(*loc, *pos, *boolean)
- }
- ast::Expression::NumberLiteral(loc, ty, n) => {
- Expression::NumberLiteral(*loc, ty.clone(), n.clone())
- }
- ast::Expression::RationalNumberLiteral(loc, ty, n) => {
- Expression::RationalNumberLiteral(*loc, ty.clone(), n.clone())
- }
- ast::Expression::Variable(loc, ty, var_no) => {
- Expression::Variable(*loc, ty.clone(), *var_no)
- }
- ast::Expression::List(loc, elements) => Expression::List(
- *loc,
- elements
- .iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>(),
- ),
- ast::Expression::GetRef(loc, ty, exp) => Expression::GetRef(
- *loc,
- ty.clone(),
- Box::new(expression(exp, cfg, contract_no, func, ns, vartab, opt)),
- ),
- }
- }
- fn memory_array_push(
- ty: &Type,
- vartab: &mut Vartable,
- array: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- value: Expression,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let address_res = vartab.temp_anonymous(ty);
- let address_arr = match expression(array, cfg, contract_no, func, ns, vartab, opt) {
- Expression::Variable(_, _, pos) => pos,
- _ => unreachable!(),
- };
- cfg.add(
- vartab,
- Instr::PushMemory {
- res: address_res,
- ty: array.ty(),
- array: address_arr,
- value: Box::new(value),
- },
- );
- Expression::Variable(*loc, ty.clone(), address_res)
- }
- fn post_incdec(
- vartab: &mut Vartable,
- ty: &Type,
- var: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- loc: &pt::Loc,
- expr: &ast::Expression,
- unchecked: &bool,
- opt: &Options,
- ) -> Expression {
- let res = vartab.temp_anonymous(ty);
- let v = expression(var, cfg, contract_no, func, ns, vartab, opt);
- let v = match var.ty() {
- Type::Ref(ty) => Expression::Load(var.loc(), ty.as_ref().clone(), Box::new(v)),
- Type::StorageRef(_, ty) => load_storage(&var.loc(), ty.as_ref(), v, cfg, vartab),
- _ => v,
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res,
- expr: v,
- },
- );
- let one = Box::new(Expression::NumberLiteral(*loc, ty.clone(), BigInt::one()));
- let expr = match expr {
- ast::Expression::PostDecrement(..) => Expression::Subtract(
- *loc,
- ty.clone(),
- *unchecked,
- Box::new(Expression::Variable(*loc, ty.clone(), res)),
- one,
- ),
- ast::Expression::PostIncrement(..) => Expression::Add(
- *loc,
- ty.clone(),
- *unchecked,
- Box::new(Expression::Variable(*loc, ty.clone(), res)),
- one,
- ),
- _ => unreachable!(),
- };
- match var {
- ast::Expression::Variable(loc, _, pos) => {
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *pos,
- expr,
- },
- );
- }
- _ => {
- let dest = expression(var, cfg, contract_no, func, ns, vartab, opt);
- let res = vartab.temp_anonymous(ty);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res,
- expr,
- },
- );
- match var.ty() {
- Type::StorageRef(..) => {
- cfg.add(
- vartab,
- Instr::SetStorage {
- value: Expression::Variable(*loc, ty.clone(), res),
- ty: ty.clone(),
- storage: dest,
- },
- );
- }
- Type::Ref(_) => {
- cfg.add(vartab, Instr::Store { pos: res, dest });
- }
- _ => unreachable!(),
- }
- }
- }
- Expression::Variable(*loc, ty.clone(), res)
- }
- fn pre_incdec(
- vartab: &mut Vartable,
- ty: &Type,
- var: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- loc: &pt::Loc,
- expr: &ast::Expression,
- unchecked: &bool,
- opt: &Options,
- ) -> Expression {
- let res = vartab.temp_anonymous(ty);
- let v = expression(var, cfg, contract_no, func, ns, vartab, opt);
- let v = match var.ty() {
- Type::Ref(ty) => Expression::Load(var.loc(), ty.as_ref().clone(), Box::new(v)),
- Type::StorageRef(_, ty) => load_storage(&var.loc(), ty.as_ref(), v, cfg, vartab),
- _ => v,
- };
- let one = Box::new(Expression::NumberLiteral(*loc, ty.clone(), BigInt::one()));
- let expr = match expr {
- ast::Expression::PreDecrement(..) => {
- Expression::Subtract(*loc, ty.clone(), *unchecked, Box::new(v), one)
- }
- ast::Expression::PreIncrement(..) => {
- Expression::Add(*loc, ty.clone(), *unchecked, Box::new(v), one)
- }
- _ => unreachable!(),
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res,
- expr,
- },
- );
- match var {
- ast::Expression::Variable(loc, _, pos) => {
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *pos,
- expr: Expression::Variable(*loc, ty.clone(), res),
- },
- );
- }
- _ => {
- let dest = expression(var, cfg, contract_no, func, ns, vartab, opt);
- match var.ty() {
- Type::StorageRef(..) => {
- cfg.add(
- vartab,
- Instr::SetStorage {
- value: Expression::Variable(*loc, ty.clone(), res),
- ty: ty.clone(),
- storage: dest,
- },
- );
- }
- Type::Ref(_) => {
- cfg.add(vartab, Instr::Store { pos: res, dest });
- }
- _ => unreachable!(),
- }
- }
- }
- Expression::Variable(*loc, ty.clone(), res)
- }
- fn expr_or(
- left: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- right: &ast::Expression,
- opt: &Options,
- ) -> Expression {
- let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
- vartab.new_dirty_tracker(ns.next_id);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "or".to_owned(),
- loc: *loc,
- },
- &Type::Bool,
- );
- let right_side = cfg.new_basic_block("or_right_side".to_string());
- let end_or = cfg.new_basic_block("or_end".to_string());
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr: Expression::BoolLiteral(*loc, true),
- },
- );
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: l,
- true_block: end_or,
- false_block: right_side,
- },
- );
- cfg.set_basic_block(right_side);
- let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr: r,
- },
- );
- cfg.add(vartab, Instr::Branch { block: end_or });
- cfg.set_basic_block(end_or);
- let mut phis = vartab.pop_dirty_tracker();
- phis.insert(pos);
- cfg.set_phis(end_or, phis);
- Expression::Variable(*loc, Type::Bool, pos)
- }
- fn and(
- left: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- right: &ast::Expression,
- opt: &Options,
- ) -> Expression {
- let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
- vartab.new_dirty_tracker(ns.next_id);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "and".to_owned(),
- loc: *loc,
- },
- &Type::Bool,
- );
- let right_side = cfg.new_basic_block("and_right_side".to_string());
- let end_and = cfg.new_basic_block("and_end".to_string());
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr: Expression::BoolLiteral(*loc, false),
- },
- );
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: l,
- true_block: right_side,
- false_block: end_and,
- },
- );
- cfg.set_basic_block(right_side);
- let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr: r,
- },
- );
- cfg.add(vartab, Instr::Branch { block: end_and });
- cfg.set_basic_block(end_and);
- let mut phis = vartab.pop_dirty_tracker();
- phis.insert(pos);
- cfg.set_phis(end_and, phis);
- Expression::Variable(*loc, Type::Bool, pos)
- }
- fn expr_assert(
- cfg: &mut ControlFlowGraph,
- args: &ast::Expression,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let true_ = cfg.new_basic_block("noassert".to_owned());
- let false_ = cfg.new_basic_block("doassert".to_owned());
- let cond = expression(args, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: true_,
- false_block: false_,
- },
- );
- cfg.set_basic_block(false_);
- cfg.add(vartab, Instr::AssertFailure { expr: None });
- cfg.set_basic_block(true_);
- Expression::Poison
- }
- fn require(
- cfg: &mut ControlFlowGraph,
- args: &[ast::Expression],
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let true_ = cfg.new_basic_block("noassert".to_owned());
- let false_ = cfg.new_basic_block("doassert".to_owned());
- let cond = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: true_,
- false_block: false_,
- },
- );
- cfg.set_basic_block(false_);
- let expr = args
- .get(1)
- .map(|s| expression(s, cfg, contract_no, func, ns, vartab, opt));
- if ns.target == Target::Solana {
- // On Solana, print the reason, do not abi encoding it
- if let Some(expr) = expr {
- cfg.add(vartab, Instr::Print { expr });
- }
- cfg.add(vartab, Instr::AssertFailure { expr: None });
- } else {
- cfg.add(vartab, Instr::AssertFailure { expr });
- }
- cfg.set_basic_block(true_);
- Expression::Poison
- }
- fn revert(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let expr = args
- .get(0)
- .map(|s| expression(s, cfg, contract_no, func, ns, vartab, opt));
- cfg.add(vartab, Instr::AssertFailure { expr });
- Expression::Poison
- }
- fn self_destruct(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let recipient = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- cfg.add(vartab, Instr::SelfDestruct { recipient });
- Expression::Poison
- }
- fn payable_send(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let address = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
- let success = vartab.temp(
- &pt::Identifier {
- loc: *loc,
- name: "success".to_owned(),
- },
- &Type::Bool,
- );
- if ns.target != Target::Ewasm {
- cfg.add(
- vartab,
- Instr::ValueTransfer {
- success: Some(success),
- address,
- value,
- },
- );
- } else {
- // Ethereum can only transfer via external call
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: Some(success),
- address: Some(address),
- accounts: None,
- payload: Expression::AllocDynamicArray(
- *loc,
- Type::DynamicBytes,
- Box::new(Expression::NumberLiteral(
- *loc,
- Type::Uint(32),
- BigInt::from(0),
- )),
- Some(vec![]),
- ),
- value,
- gas: Expression::NumberLiteral(*loc, Type::Uint(64), BigInt::from(i64::MAX)),
- callty: CallTy::Regular,
- },
- );
- }
- Expression::Variable(*loc, Type::Bool, success)
- }
- fn payable_transfer(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let address = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
- if ns.target != Target::Ewasm {
- cfg.add(
- vartab,
- Instr::ValueTransfer {
- success: None,
- address,
- value,
- },
- );
- } else {
- // Ethereum can only transfer via external call
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: None,
- accounts: None,
- address: Some(address),
- payload: Expression::AllocDynamicArray(
- *loc,
- Type::DynamicBytes,
- Box::new(Expression::NumberLiteral(
- *loc,
- Type::Uint(32),
- BigInt::from(0),
- )),
- Some(vec![]),
- ),
- value,
- gas: Expression::NumberLiteral(*loc, Type::Uint(64), BigInt::from(i64::MAX)),
- callty: CallTy::Regular,
- },
- );
- }
- Expression::Poison
- }
- fn abi_encode(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let tys = args.iter().map(|a| a.ty()).collect();
- let args = args
- .iter()
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- let res = vartab.temp(
- &pt::Identifier {
- loc: *loc,
- name: "encoded".to_owned(),
- },
- &Type::DynamicBytes,
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res,
- expr: Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![],
- args,
- },
- },
- );
- Expression::Variable(*loc, Type::DynamicBytes, res)
- }
- fn abi_encode_packed(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let tys = args.iter().map(|a| a.ty()).collect();
- let packed = args
- .iter()
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- let res = vartab.temp(
- &pt::Identifier {
- loc: *loc,
- name: "encoded".to_owned(),
- },
- &Type::DynamicBytes,
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res,
- expr: Expression::AbiEncode {
- loc: *loc,
- tys,
- packed,
- args: vec![],
- },
- },
- );
- Expression::Variable(*loc, Type::DynamicBytes, res)
- }
- fn abi_encode_with_selector(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let mut tys: Vec<Type> = args.iter().skip(1).map(|a| a.ty()).collect();
- let mut args_iter = args.iter();
- let selector = expression(
- args_iter.next().unwrap(),
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- );
- let args = args_iter
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- let res = vartab.temp(
- &pt::Identifier {
- loc: *loc,
- name: "encoded".to_owned(),
- },
- &Type::DynamicBytes,
- );
- tys.insert(0, Type::Bytes(4));
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res,
- expr: Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![selector],
- args,
- },
- },
- );
- Expression::Variable(*loc, Type::DynamicBytes, res)
- }
- fn abi_encode_with_signature(
- args: &[ast::Expression],
- loc: &pt::Loc,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let mut tys: Vec<Type> = args.iter().skip(1).map(|a| a.ty()).collect();
- let mut args_iter = args.iter();
- let hash = ast::Expression::Builtin(
- *loc,
- vec![Type::Bytes(32)],
- ast::Builtin::Keccak256,
- vec![args_iter.next().unwrap().clone()],
- );
- let hash = expression(&hash, cfg, contract_no, func, ns, vartab, opt);
- let selector = hash.cast(&Type::Bytes(4), ns);
- let args = args_iter
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- let res = vartab.temp(
- &pt::Identifier {
- loc: *loc,
- name: "encoded".to_owned(),
- },
- &Type::DynamicBytes,
- );
- tys.insert(0, Type::Bytes(4));
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res,
- expr: Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![selector],
- args,
- },
- },
- );
- Expression::Variable(*loc, Type::DynamicBytes, res)
- }
- fn abi_encode_call(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let mut tys: Vec<Type> = args.iter().skip(1).map(|a| a.ty()).collect();
- let mut args_iter = args.iter();
- let selector = expression(
- &ast::Expression::Builtin(
- *loc,
- vec![Type::Bytes(4)],
- ast::Builtin::FunctionSelector,
- vec![args_iter.next().unwrap().clone()],
- ),
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- );
- let args = args_iter
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- let res = vartab.temp(
- &pt::Identifier {
- loc: *loc,
- name: "encoded".to_owned(),
- },
- &Type::DynamicBytes,
- );
- tys.insert(0, Type::Bytes(4));
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res,
- expr: Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![selector],
- args,
- },
- },
- );
- Expression::Variable(*loc, Type::DynamicBytes, res)
- }
- fn builtin_ewasm_gasprice(
- loc: &pt::Loc,
- expr: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let ty = Type::Value;
- let gasprice = Expression::Builtin(*loc, vec![ty.clone()], Builtin::Gasprice, vec![]);
- let units = expression(&expr[0], cfg, contract_no, func, ns, vartab, opt);
- Expression::Multiply(*loc, ty, true, Box::new(units), Box::new(gasprice))
- }
- fn expr_builtin(
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- tys: &[Type],
- builtin: &ast::Builtin,
- opt: &Options,
- ) -> Expression {
- match builtin {
- ast::Builtin::WriteInt8
- | ast::Builtin::WriteInt16LE
- | ast::Builtin::WriteInt32LE
- | ast::Builtin::WriteInt64LE
- | ast::Builtin::WriteInt128LE
- | ast::Builtin::WriteInt256LE
- | ast::Builtin::WriteAddress
- | ast::Builtin::WriteUint16LE
- | ast::Builtin::WriteUint32LE
- | ast::Builtin::WriteUint64LE
- | ast::Builtin::WriteUint128LE
- | ast::Builtin::WriteUint256LE => {
- let buf = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let offset = expression(&args[2], cfg, contract_no, func, ns, vartab, opt);
- // range check
- let cond = Expression::LessEqual(
- *loc,
- Box::new(Expression::Add(
- *loc,
- Type::Uint(32),
- false,
- Box::new(offset.clone()),
- Box::new(Expression::NumberLiteral(
- *loc,
- Type::Uint(32),
- BigInt::from(args[1].ty().bits(ns) / 8),
- )),
- )),
- Box::new(Expression::Builtin(
- *loc,
- vec![Type::Uint(32)],
- Builtin::ArrayLength,
- vec![buf.clone()],
- )),
- );
- let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
- let in_bounds = cfg.new_basic_block("in_bounds".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: in_bounds,
- false_block: out_of_bounds,
- },
- );
- cfg.set_basic_block(out_of_bounds);
- cfg.add(vartab, Instr::AssertFailure { expr: None });
- cfg.set_basic_block(in_bounds);
- let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
- cfg.add(vartab, Instr::WriteBuffer { buf, value, offset });
- Expression::Undefined(tys[0].clone())
- }
- ast::Builtin::ReadInt8
- | ast::Builtin::ReadInt16LE
- | ast::Builtin::ReadInt32LE
- | ast::Builtin::ReadInt64LE
- | ast::Builtin::ReadInt128LE
- | ast::Builtin::ReadInt256LE
- | ast::Builtin::ReadAddress
- | ast::Builtin::ReadUint16LE
- | ast::Builtin::ReadUint32LE
- | ast::Builtin::ReadUint64LE
- | ast::Builtin::ReadUint128LE
- | ast::Builtin::ReadUint256LE => {
- let buf = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let offset = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
- // range check
- let cond = Expression::LessEqual(
- *loc,
- Box::new(Expression::Add(
- *loc,
- Type::Uint(32),
- false,
- Box::new(offset.clone()),
- Box::new(Expression::NumberLiteral(
- *loc,
- Type::Uint(32),
- BigInt::from(tys[0].bits(ns) / 8),
- )),
- )),
- Box::new(Expression::Builtin(
- *loc,
- vec![Type::Uint(32)],
- Builtin::ArrayLength,
- vec![buf.clone()],
- )),
- );
- let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
- let in_bounds = cfg.new_basic_block("in_bounds".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: in_bounds,
- false_block: out_of_bounds,
- },
- );
- cfg.set_basic_block(out_of_bounds);
- cfg.add(vartab, Instr::AssertFailure { expr: None });
- cfg.set_basic_block(in_bounds);
- Expression::Builtin(*loc, tys.to_vec(), builtin.into(), vec![buf, offset])
- }
- _ => {
- let args = args
- .iter()
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- Expression::Builtin(*loc, tys.to_vec(), builtin.into(), args)
- }
- }
- }
- fn alloc_dynamic_array(
- size: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- ty: &Type,
- init: &Option<Vec<u8>>,
- opt: &Options,
- ) -> Expression {
- let size = expression(size, cfg, contract_no, func, ns, vartab, opt);
- Expression::AllocDynamicArray(*loc, ty.clone(), Box::new(size), init.clone())
- }
- fn add(
- loc: &pt::Loc,
- ty: &Type,
- unchecked: &bool,
- left: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- right: &ast::Expression,
- opt: &Options,
- ) -> Expression {
- Expression::Add(
- *loc,
- ty.clone(),
- *unchecked,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- )
- }
- fn substract(
- loc: &pt::Loc,
- ty: &Type,
- unchecked: &bool,
- left: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- right: &ast::Expression,
- opt: &Options,
- ) -> Expression {
- Expression::Subtract(
- *loc,
- ty.clone(),
- *unchecked,
- Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- )
- }
- fn checking_trunc(
- loc: &pt::Loc,
- expr: &ast::Expression,
- ty: &Type,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let bits = match ty {
- Type::Uint(bits) => *bits as u32,
- Type::Value => (ns.value_length as u32 * 8),
- _ => unreachable!(),
- };
- let source_ty = expr.ty();
- let overflow = Expression::NumberLiteral(*loc, source_ty.clone(), BigInt::from(2u32).pow(bits));
- let pos = vartab.temp(
- &pt::Identifier {
- name: "value".to_owned(),
- loc: *loc,
- },
- &source_ty,
- );
- let expr = expression(expr, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr,
- },
- );
- let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
- let in_bounds = cfg.new_basic_block("in_bounds".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: Expression::MoreEqual(
- *loc,
- Box::new(Expression::Variable(*loc, source_ty.clone(), pos)),
- Box::new(overflow),
- ),
- true_block: out_of_bounds,
- false_block: in_bounds,
- },
- );
- cfg.set_basic_block(out_of_bounds);
- cfg.add(vartab, Instr::AssertFailure { expr: None });
- cfg.set_basic_block(in_bounds);
- Expression::Trunc(
- *loc,
- ty.clone(),
- Box::new(Expression::Variable(*loc, source_ty, pos)),
- )
- }
- fn format_string(
- args: &[(FormatArg, ast::Expression)],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- loc: &pt::Loc,
- opt: &Options,
- ) -> Expression {
- let args = args
- .iter()
- .map(|(spec, arg)| {
- (
- *spec,
- expression(arg, cfg, contract_no, func, ns, vartab, opt),
- )
- })
- .collect();
- Expression::FormatString(*loc, args)
- }
- fn ternary(
- loc: &pt::Loc,
- ty: &Type,
- cond: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- left: &ast::Expression,
- right: &ast::Expression,
- opt: &Options,
- ) -> Expression {
- let cond = expression(cond, cfg, contract_no, func, ns, vartab, opt);
- vartab.new_dirty_tracker(ns.next_id);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "ternary_result".to_owned(),
- loc: *loc,
- },
- ty,
- );
- let left_block = cfg.new_basic_block("left_value".to_string());
- let right_block = cfg.new_basic_block("right_value".to_string());
- let done_block = cfg.new_basic_block("ternary_done".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: left_block,
- false_block: right_block,
- },
- );
- cfg.set_basic_block(left_block);
- let expr = expression(left, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr,
- },
- );
- cfg.add(vartab, Instr::Branch { block: done_block });
- cfg.set_basic_block(right_block);
- let expr = expression(right, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr,
- },
- );
- cfg.add(vartab, Instr::Branch { block: done_block });
- cfg.set_basic_block(done_block);
- let mut phis = vartab.pop_dirty_tracker();
- phis.insert(pos);
- cfg.set_phis(done_block, phis);
- Expression::Variable(*loc, ty.clone(), pos)
- }
- fn interfaceid(ns: &Namespace, contract_no: &usize, loc: &pt::Loc) -> Expression {
- let mut id: u32 = 0;
- for func_no in &ns.contracts[*contract_no].functions {
- let func = &ns.functions[*func_no];
- if func.ty == pt::FunctionTy::Function {
- id ^= func.selector();
- }
- }
- Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::from(id))
- }
- pub fn assign_single(
- left: &ast::Expression,
- cfg_right: Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- match left {
- ast::Expression::Variable(loc, ty, pos) => {
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *pos,
- expr: cfg_right,
- },
- );
- Expression::Variable(*loc, ty.clone(), *pos)
- }
- _ => {
- let left_ty = left.ty();
- let ty = left_ty.deref_memory();
- let pos = vartab.temp_anonymous(ty);
- // Set a subscript in storage bytes needs special handling
- let set_storage_bytes = if let ast::Expression::Subscript(_, _, array_ty, _, _) = &left
- {
- array_ty.is_storage_bytes()
- } else {
- false
- };
- let dest = expression(left, cfg, contract_no, func, ns, vartab, opt);
- let cfg_right =
- if !left_ty.is_contract_storage() && cfg_right.ty().is_fixed_reference_type() {
- Expression::Load(pt::Loc::Codegen, cfg_right.ty(), Box::new(cfg_right))
- } else {
- cfg_right
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr: cfg_right,
- },
- );
- match left_ty {
- Type::StorageRef(..) if set_storage_bytes => {
- if let Expression::Subscript(_, _, _, array, index) = dest {
- // Set a byte in a byte array
- cfg.add(
- vartab,
- Instr::SetStorageBytes {
- value: Expression::Variable(left.loc(), ty.clone(), pos),
- storage: *array,
- offset: *index,
- },
- );
- } else {
- unreachable!();
- }
- }
- Type::StorageRef(..) => {
- cfg.add(
- vartab,
- Instr::SetStorage {
- value: Expression::Variable(left.loc(), ty.clone(), pos),
- ty: ty.deref_any().clone(),
- storage: dest,
- },
- );
- }
- Type::Ref(_) => {
- cfg.add(vartab, Instr::Store { pos, dest });
- }
- _ => unreachable!(),
- }
- Expression::Variable(left.loc(), ty.clone(), pos)
- }
- }
- }
- /// Convert a function call expression to CFG in expression context
- pub fn emit_function_call(
- expr: &ast::Expression,
- callee_contract_no: usize,
- cfg: &mut ControlFlowGraph,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Vec<Expression> {
- match expr {
- ast::Expression::InternalFunctionCall { function, args, .. } => {
- if let ast::Expression::InternalFunction {
- function_no,
- signature,
- ..
- } = function.as_ref()
- {
- let args = args
- .iter()
- .map(|a| expression(a, cfg, callee_contract_no, func, ns, vartab, opt))
- .collect();
- let function_no = if let Some(signature) = signature {
- &ns.contracts[callee_contract_no].virtual_functions[signature]
- } else {
- function_no
- };
- let cfg_no = ns.contracts[callee_contract_no].all_functions[function_no];
- let ftype = &ns.functions[*function_no];
- if !ftype.returns.is_empty() {
- let mut res = Vec::new();
- let mut returns = Vec::new();
- let mut return_tys = Vec::new();
- for ret in &*ftype.returns {
- let id = pt::Identifier {
- loc: ret.loc,
- name: ret.name_as_str().to_owned(),
- };
- let temp_pos = vartab.temp(&id, &ret.ty);
- return_tys.push(ret.ty.clone());
- res.push(temp_pos);
- returns.push(Expression::Variable(id.loc, ret.ty.clone(), temp_pos));
- }
- cfg.add(
- vartab,
- Instr::Call {
- res,
- call: InternalCallTy::Static(cfg_no),
- args,
- return_tys,
- },
- );
- returns
- } else {
- cfg.add(
- vartab,
- Instr::Call {
- res: Vec::new(),
- return_tys: Vec::new(),
- call: InternalCallTy::Static(cfg_no),
- args,
- },
- );
- vec![Expression::Poison]
- }
- } else if let Type::InternalFunction { returns, .. } = function.ty().deref_any() {
- let cfg_expr = expression(function, cfg, callee_contract_no, func, ns, vartab, opt);
- let args = args
- .iter()
- .map(|a| expression(a, cfg, callee_contract_no, func, ns, vartab, opt))
- .collect();
- if !returns.is_empty() {
- let mut res = Vec::new();
- let mut return_values = Vec::new();
- let mut return_tys = Vec::new();
- for ty in returns {
- let id = pt::Identifier {
- loc: pt::Loc::Codegen,
- name: String::new(),
- };
- let temp_pos = vartab.temp(&id, ty);
- res.push(temp_pos);
- return_tys.push(ty.clone());
- return_values.push(Expression::Variable(id.loc, ty.clone(), temp_pos));
- }
- cfg.add(
- vartab,
- Instr::Call {
- res,
- call: InternalCallTy::Dynamic(cfg_expr),
- return_tys,
- args,
- },
- );
- return_values
- } else {
- cfg.add(
- vartab,
- Instr::Call {
- res: Vec::new(),
- return_tys: Vec::new(),
- call: InternalCallTy::Dynamic(cfg_expr),
- args,
- },
- );
- vec![Expression::Poison]
- }
- } else {
- unreachable!();
- }
- }
- ast::Expression::ExternalFunctionCallRaw {
- loc,
- address,
- args,
- call_args,
- ty,
- } => {
- let args = expression(args, cfg, callee_contract_no, func, ns, vartab, opt);
- let address = expression(address, cfg, callee_contract_no, func, ns, vartab, opt);
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, callee_contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, callee_contract_no, func, ns, vartab, opt)
- } else {
- Expression::NumberLiteral(pt::Loc::Codegen, Type::Value, BigInt::zero())
- };
- let accounts = call_args
- .accounts
- .as_ref()
- .map(|expr| expression(expr, cfg, callee_contract_no, func, ns, vartab, opt));
- let success = vartab.temp_name("success", &Type::Bool);
- let (payload, address) = if ns.target == Target::Solana && call_args.accounts.is_none()
- {
- (
- Expression::AbiEncode {
- loc: *loc,
- packed: vec![
- address,
- Expression::Builtin(
- *loc,
- vec![Type::Address(false)],
- Builtin::GetAddress,
- Vec::new(),
- ),
- value.clone(),
- Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
- Expression::NumberLiteral(*loc, Type::Bytes(1), BigInt::zero()),
- args,
- ],
- args: Vec::new(),
- tys: vec![
- Type::Address(false),
- Type::Address(false),
- Type::Uint(64),
- Type::Bytes(4),
- Type::Bytes(1),
- Type::DynamicBytes,
- ],
- },
- None,
- )
- } else {
- (args, Some(address))
- };
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: Some(success),
- address,
- payload,
- value,
- accounts,
- gas,
- callty: ty.clone(),
- },
- );
- vec![
- Expression::Variable(*loc, Type::Bool, success),
- Expression::ReturnData(*loc),
- ]
- }
- ast::Expression::ExternalFunctionCall {
- loc,
- function,
- args,
- returns,
- call_args,
- ..
- } => {
- if let ast::Expression::ExternalFunction {
- function_no,
- address,
- ..
- } = function.as_ref()
- {
- let ftype = &ns.functions[*function_no];
- let mut tys: Vec<Type> = args.iter().map(|a| a.ty()).collect();
- let args = args
- .iter()
- .map(|a| expression(a, cfg, callee_contract_no, func, ns, vartab, opt))
- .collect();
- let address = expression(address, cfg, callee_contract_no, func, ns, vartab, opt);
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, callee_contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, callee_contract_no, func, ns, vartab, opt)
- } else {
- Expression::NumberLiteral(pt::Loc::Codegen, Type::Value, BigInt::zero())
- };
- let dest_func = &ns.functions[*function_no];
- tys.insert(0, Type::Bytes(4));
- let (payload, address) = if ns.target == Target::Solana {
- tys.insert(0, Type::Address(false));
- tys.insert(1, Type::Address(false));
- tys.insert(2, Type::Uint(64));
- tys.insert(3, Type::Bytes(4));
- tys.insert(4, Type::Bytes(1));
- (
- Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![
- address,
- Expression::Builtin(
- *loc,
- vec![Type::Address(false)],
- Builtin::GetAddress,
- Vec::new(),
- ),
- value.clone(),
- Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
- Expression::NumberLiteral(*loc, Type::Bytes(1), BigInt::zero()),
- Expression::NumberLiteral(
- *loc,
- Type::Bytes(4),
- BigInt::from(dest_func.selector()),
- ),
- ],
- args,
- },
- None,
- )
- } else {
- (
- Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![Expression::NumberLiteral(
- *loc,
- Type::Bytes(4),
- BigInt::from(dest_func.selector()),
- )],
- args,
- },
- Some(address),
- )
- };
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: None,
- accounts: None,
- address,
- payload,
- value,
- gas,
- callty: CallTy::Regular,
- },
- );
- // If the first element of returns is Void, we can discard the returns
- if !ftype.returns.is_empty() && returns[0] != Type::Void {
- let mut returns = Vec::new();
- let mut res = Vec::new();
- for ret in &*ftype.returns {
- let id = pt::Identifier {
- loc: ret.loc,
- name: ret.name_as_str().to_owned(),
- };
- let temp_pos = vartab.temp(&id, &ret.ty);
- res.push(temp_pos);
- returns.push(Expression::Variable(id.loc, ret.ty.clone(), temp_pos));
- }
- cfg.add(
- vartab,
- Instr::AbiDecode {
- res,
- selector: None,
- exception_block: None,
- tys: (*ftype.returns).clone(),
- data: Expression::ReturnData(*loc),
- },
- );
- returns
- } else {
- vec![Expression::Poison]
- }
- } else if let Type::ExternalFunction {
- returns: func_returns,
- ..
- } = function.ty()
- {
- let mut tys: Vec<Type> = args.iter().map(|a| a.ty()).collect();
- let args = args
- .iter()
- .map(|a| expression(a, cfg, callee_contract_no, func, ns, vartab, opt))
- .collect();
- let function = expression(function, cfg, callee_contract_no, func, ns, vartab, opt);
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, callee_contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, callee_contract_no, func, ns, vartab, opt)
- } else {
- Expression::NumberLiteral(pt::Loc::Codegen, Type::Value, BigInt::zero())
- };
- let selector = Expression::Builtin(
- *loc,
- vec![Type::Bytes(4)],
- Builtin::FunctionSelector,
- vec![function.clone()],
- );
- let address = Expression::Builtin(
- *loc,
- vec![Type::Address(false)],
- Builtin::ExternalFunctionAddress,
- vec![function],
- );
- let (payload, address) = if ns.target == Target::Solana {
- tys.insert(0, Type::Address(false));
- tys.insert(1, Type::Address(false));
- tys.insert(2, Type::Uint(64));
- tys.insert(3, Type::Bytes(4));
- tys.insert(4, Type::Bytes(1));
- tys.insert(5, Type::Bytes(4));
- (
- Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![
- address,
- Expression::Builtin(
- *loc,
- vec![Type::Address(false)],
- Builtin::GetAddress,
- Vec::new(),
- ),
- value.clone(),
- Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
- Expression::NumberLiteral(*loc, Type::Bytes(1), BigInt::zero()),
- selector,
- ],
- args,
- },
- None,
- )
- } else {
- tys.insert(0, Type::Bytes(4));
- (
- Expression::AbiEncode {
- loc: *loc,
- tys,
- packed: vec![selector],
- args,
- },
- Some(address),
- )
- };
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: None,
- accounts: None,
- address,
- payload,
- value,
- gas,
- callty: CallTy::Regular,
- },
- );
- if !func_returns.is_empty() && returns[0] != Type::Void {
- let mut returns = Vec::new();
- let mut res = Vec::new();
- let mut tys = Vec::new();
- for ty in func_returns {
- let temp_pos = vartab.temp_anonymous(&ty);
- res.push(temp_pos);
- returns.push(Expression::Variable(pt::Loc::Codegen, ty.clone(), temp_pos));
- tys.push(Parameter {
- loc: pt::Loc::Codegen,
- ty,
- ty_loc: Some(pt::Loc::Codegen),
- id: None,
- indexed: false,
- readonly: false,
- });
- }
- cfg.add(
- vartab,
- Instr::AbiDecode {
- res,
- selector: None,
- exception_block: None,
- tys,
- data: Expression::ReturnData(*loc),
- },
- );
- returns
- } else {
- vec![Expression::Poison]
- }
- } else {
- unreachable!();
- }
- }
- ast::Expression::Builtin(loc, tys, ast::Builtin::AbiDecode, args) => {
- let data = expression(&args[0], cfg, callee_contract_no, func, ns, vartab, opt);
- let mut returns = Vec::new();
- let mut res = Vec::new();
- for ret in tys {
- let temp_pos = vartab.temp_anonymous(ret);
- res.push(temp_pos);
- returns.push(Expression::Variable(*loc, ret.clone(), temp_pos));
- }
- cfg.add(
- vartab,
- Instr::AbiDecode {
- res,
- selector: None,
- exception_block: None,
- tys: tys
- .iter()
- .map(|ty| Parameter {
- id: None,
- loc: *loc,
- ty: ty.clone(),
- ty_loc: Some(*loc),
- indexed: false,
- readonly: false,
- })
- .collect(),
- data,
- },
- );
- returns
- }
- _ => unreachable!(),
- }
- }
- pub fn default_gas(ns: &Namespace) -> Expression {
- Expression::NumberLiteral(
- pt::Loc::Codegen,
- Type::Uint(64),
- // See EIP150
- if ns.target == Target::Ewasm {
- BigInt::from(i64::MAX)
- } else {
- BigInt::zero()
- },
- )
- }
- /// Codegen for an array subscript expression
- fn array_subscript(
- loc: &pt::Loc,
- elem_ty: &Type,
- array_ty: &Type,
- array: &ast::Expression,
- index: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- if array_ty.is_storage_bytes() {
- return Expression::Subscript(
- *loc,
- elem_ty.clone(),
- array_ty.clone(),
- Box::new(expression(array, cfg, contract_no, func, ns, vartab, opt)),
- Box::new(expression(index, cfg, contract_no, func, ns, vartab, opt)),
- );
- }
- if array_ty.is_mapping() {
- let array = expression(array, cfg, contract_no, func, ns, vartab, opt);
- let index = expression(index, cfg, contract_no, func, ns, vartab, opt);
- return if ns.target == Target::Solana {
- Expression::Subscript(
- *loc,
- elem_ty.clone(),
- array_ty.clone(),
- Box::new(array),
- Box::new(index),
- )
- } else {
- Expression::Keccak256(*loc, array_ty.clone(), vec![array, index])
- };
- }
- let mut array = expression(array, cfg, contract_no, func, ns, vartab, opt);
- let index_ty = index.ty();
- let index = expression(index, cfg, contract_no, func, ns, vartab, opt);
- let index_loc = index.loc();
- let index_width = index_ty.bits(ns);
- let array_length = match array_ty.deref_any() {
- Type::Bytes(n) => {
- let ast_bigint = bigint_to_expression(
- &array.loc(),
- &BigInt::from(*n),
- ns,
- &mut Vec::new(),
- ResolveTo::Unknown,
- )
- .unwrap();
- expression(&ast_bigint, cfg, contract_no, func, ns, vartab, opt)
- }
- Type::Array(..) => match array_ty.array_length() {
- None => {
- if let Type::StorageRef(..) = array_ty {
- if ns.target == Target::Solana {
- Expression::StorageArrayLength {
- loc: *loc,
- ty: ns.storage_type(),
- array: Box::new(array.clone()),
- elem_ty: array_ty.storage_array_elem().deref_into(),
- }
- } else {
- let array_length =
- load_storage(loc, &Type::Uint(256), array.clone(), cfg, vartab);
- array = Expression::Keccak256(*loc, Type::Uint(256), vec![array]);
- array_length
- }
- } else {
- Expression::Builtin(
- *loc,
- vec![Type::Uint(32)],
- Builtin::ArrayLength,
- vec![array.clone()],
- )
- }
- }
- Some(l) => {
- let ast_big_int =
- bigint_to_expression(loc, l, ns, &mut Vec::new(), ResolveTo::Unknown).unwrap();
- expression(&ast_big_int, cfg, contract_no, func, ns, vartab, opt)
- }
- },
- Type::DynamicBytes => Expression::Builtin(
- *loc,
- vec![Type::Uint(32)],
- Builtin::ArrayLength,
- vec![array.clone()],
- ),
- _ => {
- unreachable!();
- }
- };
- let array_width = array_length.ty().bits(ns);
- let width = std::cmp::max(array_width, index.ty().bits(ns));
- let coerced_ty = Type::Uint(width);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "index".to_owned(),
- loc: *loc,
- },
- &coerced_ty,
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: pos,
- expr: index.cast(&coerced_ty, ns),
- },
- );
- // If the array is fixed length and the index also constant, the
- // branch will be optimized away.
- let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
- let in_bounds = cfg.new_basic_block("in_bounds".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: Expression::MoreEqual(
- *loc,
- Box::new(Expression::Variable(index_loc, coerced_ty.clone(), pos)),
- Box::new(array_length.cast(&coerced_ty, ns)),
- ),
- true_block: out_of_bounds,
- false_block: in_bounds,
- },
- );
- cfg.set_basic_block(out_of_bounds);
- cfg.add(vartab, Instr::AssertFailure { expr: None });
- cfg.set_basic_block(in_bounds);
- if let Type::StorageRef(_, ty) = &array_ty {
- let elem_ty = ty.storage_array_elem();
- let slot_ty = ns.storage_type();
- if ns.target == Target::Solana {
- if ty.array_length().is_some() && ty.is_sparse_solana(ns) {
- let index =
- Expression::Variable(index_loc, coerced_ty, pos).cast(&Type::Uint(256), ns);
- Expression::Subscript(
- *loc,
- elem_ty,
- array_ty.clone(),
- Box::new(array),
- Box::new(index),
- )
- } else {
- let index = Expression::Variable(index_loc, coerced_ty, pos).cast(&slot_ty, ns);
- if ty.array_length().is_some() {
- // fixed length array
- let elem_size = elem_ty.deref_any().size_of(ns);
- Expression::Add(
- *loc,
- elem_ty,
- true,
- Box::new(array),
- Box::new(Expression::Multiply(
- *loc,
- slot_ty.clone(),
- true,
- Box::new(index),
- Box::new(Expression::NumberLiteral(*loc, slot_ty, elem_size)),
- )),
- )
- } else {
- Expression::Subscript(
- *loc,
- elem_ty,
- array_ty.clone(),
- Box::new(array),
- Box::new(index),
- )
- }
- }
- } else {
- let elem_size = elem_ty.storage_slots(ns);
- if let Expression::NumberLiteral(_, _, arr_length) = &array_length {
- if arr_length.mul(elem_size.clone()).to_u64().is_some() {
- // we need to calculate the storage offset. If this can be done with 64 bit
- // arithmetic it will be much more efficient on wasm
- return Expression::Add(
- *loc,
- elem_ty,
- true,
- Box::new(array),
- Box::new(Expression::ZeroExt(
- *loc,
- slot_ty,
- Box::new(Expression::Multiply(
- *loc,
- Type::Uint(64),
- true,
- Box::new(
- Expression::Variable(index_loc, coerced_ty, pos)
- .cast(&Type::Uint(64), ns),
- ),
- Box::new(Expression::NumberLiteral(
- *loc,
- Type::Uint(64),
- elem_size,
- )),
- )),
- )),
- );
- }
- }
- array_offset(
- loc,
- array,
- Expression::Variable(index_loc, coerced_ty, pos).cast(&ns.storage_type(), ns),
- elem_ty,
- ns,
- )
- }
- } else {
- match array_ty.deref_memory() {
- Type::Bytes(array_length) => {
- let res_ty = Type::Bytes(1);
- let from_ty = Type::Bytes(*array_length);
- let index_ty = Type::Uint(*array_length as u16 * 8);
- let to_width = array_ty.bits(ns);
- let shift_arg_raw = Expression::Variable(index_loc, coerced_ty.clone(), pos);
- let shift_arg = if index_width == to_width {
- shift_arg_raw
- } else if index_width < to_width && array_ty.is_signed_int() {
- Expression::SignExt(*loc, array_ty.clone(), Box::new(shift_arg_raw))
- } else if index_width < to_width && !array_ty.is_signed_int() {
- Expression::ZeroExt(*loc, array_ty.clone(), Box::new(shift_arg_raw))
- } else {
- Expression::Trunc(*loc, array_ty.clone(), Box::new(shift_arg_raw))
- };
- Expression::Trunc(
- *loc,
- res_ty,
- Box::new(Expression::ShiftRight(
- *loc,
- from_ty,
- Box::new(array),
- // shift by (array_length - 1 - index) * 8
- Box::new(Expression::ShiftLeft(
- *loc,
- index_ty.clone(),
- Box::new(Expression::Subtract(
- *loc,
- index_ty.clone(),
- true,
- Box::new(Expression::NumberLiteral(
- *loc,
- index_ty.clone(),
- BigInt::from_u8(array_length - 1).unwrap(),
- )),
- Box::new(shift_arg),
- )),
- Box::new(Expression::NumberLiteral(
- *loc,
- index_ty,
- BigInt::from_u8(3).unwrap(),
- )),
- )),
- false,
- )),
- )
- }
- Type::Array(_, dim) if dim.last().unwrap().is_some() => Expression::Subscript(
- *loc,
- elem_ty.clone(),
- array_ty.clone(),
- Box::new(array),
- Box::new(Expression::Variable(index_loc, coerced_ty, pos)),
- ),
- Type::DynamicBytes | Type::Array(..) => Expression::Subscript(
- *loc,
- elem_ty.clone(),
- array_ty.clone(),
- Box::new(array),
- Box::new(Expression::Variable(index_loc, coerced_ty, pos)),
- ),
- _ => {
- // should not happen as type-checking already done
- unreachable!();
- }
- }
- }
- }
- fn string_location(
- loc: &StringLocation<ast::Expression>,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> StringLocation<Expression> {
- match loc {
- StringLocation::RunTime(s) => StringLocation::RunTime(Box::new(expression(
- s,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- ))),
- StringLocation::CompileTime(vec) => StringLocation::CompileTime(vec.clone()),
- }
- }
- // Generate a load from storage instruction
- pub fn load_storage(
- loc: &pt::Loc,
- ty: &Type,
- storage: Expression,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ) -> Expression {
- let res = vartab.temp_anonymous(ty);
- cfg.add(
- vartab,
- Instr::LoadStorage {
- res,
- ty: ty.clone(),
- storage,
- },
- );
- Expression::Variable(*loc, ty.clone(), res)
- }
|