| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647 |
- // SPDX-License-Identifier: Apache-2.0
- use super::encoding::{abi_decode, abi_encode};
- 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::array_boundary::handle_array_assign;
- use crate::codegen::constructor::call_constructor;
- use crate::codegen::error_msg_with_loc;
- use crate::codegen::unused_variable::should_remove_assignment;
- use crate::codegen::{Builtin, Expression};
- use crate::sema::{
- ast,
- ast::{
- ArrayLength, CallTy, FormatArg, Function, Namespace, RetrieveType, StringLocation,
- StructType, Type,
- },
- diagnostics::Diagnostics,
- eval::{eval_const_number, eval_const_rational},
- expression::integers::bigint_to_expression,
- 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, Loc};
- use std::{cmp::Ordering, 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 {
- loc,
- contract_no: var_contract_no,
- var_no,
- ..
- } => {
- // base storage variables should precede contract variables, not overlap
- ns.contracts[contract_no].get_storage_slot(*loc, *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,
- } => subtract(
- 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: *loc,
- ty: ty.clone(),
- value: r.to_integer(),
- }
- } else {
- Expression::Multiply {
- loc: *loc,
- ty: ty.clone(),
- overflowing: *unchecked,
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: 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(ns) {
- Expression::SignedDivide {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(l),
- right: Box::new(r),
- }
- } else {
- Expression::UnsignedDivide {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(l),
- right: 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(ns) {
- Expression::SignedModulo {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(l),
- right: Box::new(r),
- }
- } else {
- Expression::UnsignedModulo {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(l),
- right: Box::new(r),
- }
- }
- }
- ast::Expression::Power {
- loc,
- ty,
- unchecked,
- base,
- exp,
- } => Expression::Power {
- loc: *loc,
- ty: ty.clone(),
- overflowing: *unchecked,
- base: Box::new(expression(base, cfg, contract_no, func, ns, vartab, opt)),
- exp: Box::new(expression(exp, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::BitwiseOr {
- loc,
- ty,
- left,
- right,
- } => Expression::BitwiseOr {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::BitwiseAnd {
- loc,
- ty,
- left,
- right,
- } => Expression::BitwiseAnd {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::BitwiseXor {
- loc,
- ty,
- left,
- right,
- } => Expression::BitwiseXor {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::ShiftLeft {
- loc,
- ty,
- left,
- right,
- } => Expression::ShiftLeft {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::ShiftRight {
- loc,
- ty,
- left,
- right,
- sign,
- } => Expression::ShiftRight {
- loc: *loc,
- ty: ty.clone(),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- signed: *sign,
- },
- ast::Expression::Equal { loc, left, right } => Expression::Equal {
- loc: *loc,
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::NotEqual { loc, left, right } => Expression::NotEqual {
- loc: *loc,
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: 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);
- Expression::More {
- loc: *loc,
- signed: l.ty().is_signed_int(ns),
- left: Box::new(l),
- right: Box::new(r),
- }
- }
- ast::Expression::MoreEqual { loc, left, right } => Expression::MoreEqual {
- loc: *loc,
- signed: left.ty().is_signed_int(ns),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: 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);
- Expression::Less {
- loc: *loc,
- signed: l.ty().is_signed_int(ns),
- left: Box::new(l),
- right: Box::new(r),
- }
- }
- ast::Expression::LessEqual { loc, left, right } => Expression::LessEqual {
- loc: *loc,
- signed: left.ty().is_signed_int(ns),
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::ConstantVariable {
- contract_no: 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 {
- contract_no: 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: *loc,
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::BitwiseNot { loc, ty, expr } => Expression::BitwiseNot {
- loc: *loc,
- ty: ty.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::Negate { loc, ty, expr } => Expression::Negate {
- loc: *loc,
- ty: ty.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::StructLiteral { loc, ty, values } => Expression::StructLiteral {
- loc: *loc,
- ty: ty.clone(),
- values: values
- .iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect(),
- },
- ast::Expression::ArrayLiteral {
- loc,
- ty,
- dimensions,
- values,
- } => Expression::ArrayLiteral {
- loc: *loc,
- ty: ty.clone(),
- dimensions: dimensions.clone(),
- values: values
- .iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect(),
- },
- ast::Expression::ConstArrayLiteral {
- loc,
- ty,
- dimensions,
- values,
- } => Expression::ConstArrayLiteral {
- loc: *loc,
- ty: ty.clone(),
- dimensions: dimensions.clone(),
- values: values
- .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 mut cfg_right = expression(right, cfg, contract_no, func, ns, vartab, opt);
- // If an assignment where the left hand side is an array, call a helper function that updates the temp variable.
- if let ast::Expression::Variable {
- ty: Type::Array(..),
- var_no,
- ..
- } = &**left
- {
- // If cfg_right is an AllocDynamicArray(_,_,size,_), update it such that it becomes AllocDynamicArray(_,_,temp_var,_) to avoid repetitive expressions in the cfg.
- cfg_right = handle_array_assign(cfg_right, cfg, vartab, *var_no);
- }
- assign_single(left, cfg_right, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::PreDecrement {
- loc,
- ty,
- unchecked,
- expr: var,
- }
- | ast::Expression::PreIncrement {
- loc,
- ty,
- unchecked,
- expr: var,
- } => pre_incdec(
- vartab,
- ty,
- var,
- cfg,
- contract_no,
- func,
- ns,
- loc,
- expr,
- *unchecked,
- opt,
- ),
- ast::Expression::PostDecrement {
- loc,
- ty,
- unchecked,
- expr: var,
- }
- | ast::Expression::PostIncrement {
- loc,
- ty,
- unchecked,
- expr: var,
- } => post_incdec(
- vartab,
- ty,
- var,
- cfg,
- contract_no,
- func,
- ns,
- loc,
- expr,
- *unchecked,
- opt,
- ),
- ast::Expression::Constructor {
- loc,
- contract_no: constructor_contract,
- constructor_no,
- args,
- call_args,
- } => {
- let address_res = vartab.temp_anonymous(&Type::Contract(*constructor_contract));
- call_constructor(
- loc,
- *constructor_contract,
- contract_no,
- constructor_no,
- args,
- call_args,
- address_res,
- None,
- func,
- ns,
- vartab,
- cfg,
- opt,
- );
- Expression::Variable {
- loc: *loc,
- ty: Type::Contract(*constructor_contract),
- var_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 {
- cfg_no: 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 Diagnostics::default(),
- ResolveTo::Type(ty),
- None,
- )
- .unwrap();
- expression(&ast_expr, cfg, contract_no, func, ns, vartab, opt)
- }
- Type::DynamicBytes | Type::String => Expression::StorageArrayLength {
- loc: *loc,
- ty: ty.clone(),
- array: Box::new(array),
- elem_ty: elem_ty.clone(),
- },
- Type::Array(_, dim) => match dim.last().unwrap() {
- ArrayLength::Dynamic => {
- 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)
- }
- }
- ArrayLength::Fixed(length) => {
- let ast_expr = bigint_to_expression(
- loc,
- length,
- ns,
- &mut Diagnostics::default(),
- ResolveTo::Type(ty),
- None,
- )
- .unwrap();
- expression(&ast_expr, cfg, contract_no, func, ns, vartab, opt)
- }
- _ => unreachable!(),
- },
- _ => unreachable!(),
- }
- }
- ast::Expression::Builtin {
- kind: ast::Builtin::ExternalFunctionAddress,
- args: 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);
- func_expr.external_function_address()
- }
- }
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::FunctionSelector,
- args: func_expr,
- ..
- } => match &func_expr[0] {
- ast::Expression::ExternalFunction { function_no, .. }
- | ast::Expression::InternalFunction { function_no, .. } => {
- let selector = ns.functions[*function_no].selector(ns, &contract_no);
- Expression::BytesLiteral {
- loc: *loc,
- ty: Type::Bytes(selector.len() as u8),
- value: selector,
- }
- }
- _ => {
- let func_expr = expression(&func_expr[0], cfg, contract_no, func, ns, vartab, opt);
- func_expr.external_function_selector()
- }
- },
- ast::Expression::InternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCallRaw { .. }
- | ast::Expression::Builtin {
- kind: 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);
- let selector = Expression::BytesLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: ns.functions[*function_no].selector(ns, &contract_no),
- };
- let struct_literal = Expression::StructLiteral {
- loc: *loc,
- ty: Type::Struct(StructType::ExternalFunction),
- values: vec![selector, address],
- };
- Expression::Cast {
- loc: *loc,
- ty: ty.clone(),
- expr: Box::new(struct_literal),
- }
- }
- ast::Expression::Subscript {
- loc,
- ty: 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,
- expr: var,
- field: field_no,
- } if ty.is_contract_storage() => {
- if let Type::Struct(struct_ty) = var.ty().deref_any() {
- let offset = if ns.target == Target::Solana {
- struct_ty.definition(ns).storage_offsets[*field_no].clone()
- } else {
- struct_ty.definition(ns).fields[..*field_no]
- .iter()
- .filter(|field| !field.infinite_size)
- .map(|field| field.ty.storage_slots(ns))
- .sum()
- };
- Expression::Add {
- loc: *loc,
- ty: ns.storage_type(),
- overflowing: true,
- left: Box::new(expression(var, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: ns.storage_type(),
- value: offset,
- }),
- }
- } else {
- unreachable!();
- }
- }
- ast::Expression::StructMember {
- loc,
- ty,
- expr: var,
- field: member,
- } => Expression::StructMember {
- loc: *loc,
- ty: ty.clone(),
- expr: Box::new(expression(var, cfg, contract_no, func, ns, vartab, opt)),
- member: *member,
- },
- ast::Expression::StringCompare { loc, left, right } => Expression::StringCompare {
- loc: *loc,
- left: string_location(left, cfg, contract_no, func, ns, vartab, opt),
- right: string_location(right, cfg, contract_no, func, ns, vartab, opt),
- },
- ast::Expression::StringConcat {
- loc,
- ty,
- left,
- right,
- } => Expression::StringConcat {
- loc: *loc,
- ty: ty.clone(),
- left: string_location(left, cfg, contract_no, func, ns, vartab, opt),
- right: 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, to, expr } => {
- checking_trunc(loc, expr, to, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Trunc { loc, to, expr } => Expression::Trunc {
- loc: *loc,
- ty: to.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::ZeroExt { loc, to, expr } => Expression::ZeroExt {
- loc: *loc,
- ty: to.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::SignExt { loc, to, expr } => Expression::SignExt {
- loc: *loc,
- ty: to.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::Cast { loc, to, expr } if matches!(to, Type::Address(_)) => {
- if let Ok((_, address)) = eval_const_number(expr, ns) {
- Expression::NumberLiteral {
- loc: *loc,
- ty: to.clone(),
- value: address,
- }
- } else {
- Expression::Cast {
- loc: *loc,
- ty: to.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- }
- }
- }
- ast::Expression::Cast { to, expr, .. }
- if matches!((expr.ty(), to), (Type::Address(_), Type::Contract(_))) =>
- {
- // Address and Contract have the same underlying type. CSE will create
- // a temporary to replace multiple casts from address to Contract, which have no
- // real purpose.
- expression(expr, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Cast { loc, to, expr }
- if matches!(to, Type::Array(..))
- && matches!(**expr, ast::Expression::ArrayLiteral { .. }) =>
- {
- let codegen_expr = expression(expr, cfg, contract_no, func, ns, vartab, opt);
- array_literal_to_memory_array(loc, &codegen_expr, to, cfg, vartab)
- }
- ast::Expression::Cast { loc, to, expr } => {
- if expr.ty() == Type::Rational {
- let (_, n) = eval_const_rational(expr, ns).unwrap();
- Expression::NumberLiteral {
- loc: *loc,
- ty: to.clone(),
- value: n.to_integer(),
- }
- } else if matches!(to, Type::String | Type::DynamicBytes)
- && matches!(expr.ty(), Type::String | Type::DynamicBytes)
- {
- expression(expr, cfg, contract_no, func, ns, vartab, opt)
- } else {
- Expression::Cast {
- loc: *loc,
- ty: to.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- }
- }
- }
- ast::Expression::BytesCast {
- loc,
- to,
- from,
- expr,
- } => Expression::BytesCast {
- loc: *loc,
- ty: to.clone(),
- from: from.clone(),
- expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::Load { loc, ty, expr: e } => Expression::Load {
- loc: *loc,
- ty: ty.clone(),
- expr: 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 {
- kind: ast::Builtin::UserTypeWrap,
- args,
- ..
- }
- | ast::Expression::Builtin {
- kind: ast::Builtin::UserTypeUnwrap,
- args,
- ..
- } => expression(&args[0], cfg, contract_no, func, ns, vartab, opt),
- ast::Expression::Builtin {
- loc,
- tys: ty,
- kind: 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,
- tys: ty,
- kind: 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 array_pos = match expression(&args[0], cfg, contract_no, func, ns, vartab, opt)
- {
- Expression::Variable { var_no, .. } => {
- vartab.set_dirty(var_no);
- var_no
- }
- _ => unreachable!(),
- };
- cfg.add(
- vartab,
- Instr::PopMemory {
- res: address_res,
- ty: args[0].ty(),
- array: array_pos,
- loc: *loc,
- },
- );
- cfg.modify_temp_array_length(*loc, true, array_pos, vartab);
- Expression::Variable {
- loc: *loc,
- ty: ty[0].clone(),
- var_no: address_res,
- }
- }
- }
- ast::Expression::Builtin {
- kind: ast::Builtin::Assert,
- args,
- ..
- } => expr_assert(cfg, &args[0], contract_no, func, ns, vartab, opt),
- ast::Expression::Builtin {
- kind: ast::Builtin::Print,
- args,
- ..
- } => {
- if opt.log_prints {
- let expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let to_print = if ns.target.is_substrate() {
- add_prefix_and_delimiter_to_print(expr)
- } else {
- expr
- };
- cfg.add(vartab, Instr::Print { expr: to_print });
- }
- Expression::Poison
- }
- ast::Expression::Builtin {
- kind: ast::Builtin::Require,
- args,
- ..
- } => require(cfg, args, contract_no, func, ns, vartab, opt, expr.loc()),
- ast::Expression::Builtin {
- kind: ast::Builtin::SelfDestruct,
- args,
- ..
- } => self_destruct(args, cfg, contract_no, func, ns, vartab, opt),
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::PayableSend,
- args,
- ..
- } => payable_send(args, cfg, contract_no, func, ns, vartab, loc, opt),
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::PayableTransfer,
- args,
- ..
- } => payable_transfer(args, cfg, contract_no, func, ns, vartab, loc, opt),
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::AbiEncode,
- args,
- ..
- } => abi_encode_many(args, cfg, contract_no, func, ns, vartab, loc, opt),
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::AbiEncodePacked,
- args,
- ..
- } => abi_encode_packed(args, cfg, contract_no, func, ns, vartab, loc, opt),
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::AbiEncodeWithSelector,
- args,
- ..
- } => abi_encode_with_selector(args, cfg, contract_no, func, ns, vartab, loc, opt),
- ast::Expression::Builtin {
- loc,
- kind: ast::Builtin::AbiEncodeWithSignature,
- args,
- ..
- } => abi_encode_with_signature(args, loc, cfg, contract_no, func, ns, vartab, opt),
- ast::Expression::Builtin {
- loc,
- kind: 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,
- kind: ast::Builtin::Gasprice,
- args: expr,
- ..
- } if expr.len() == 1 && ns.target == Target::EVM => {
- builtin_evm_gasprice(loc, expr, cfg, contract_no, func, ns, vartab, opt)
- }
- ast::Expression::Builtin {
- loc,
- tys,
- kind,
- args,
- } => expr_builtin(
- args,
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- loc,
- tys,
- *kind,
- opt,
- ),
- ast::Expression::FormatString { loc, format: args } => {
- format_string(args, cfg, contract_no, func, ns, vartab, loc, opt)
- }
- ast::Expression::AllocDynamicBytes {
- loc,
- ty,
- length: size,
- init,
- } => alloc_dynamic_array(size, cfg, contract_no, func, ns, vartab, loc, ty, init, opt),
- ast::Expression::ConditionalOperator {
- loc,
- ty,
- cond,
- true_option: left,
- false_option: right,
- } => conditional_operator(
- 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: *loc,
- value: *value,
- },
- ast::Expression::BytesLiteral { loc, ty, value } => Expression::BytesLiteral {
- loc: *loc,
- ty: ty.clone(),
- value: value.clone(),
- },
- ast::Expression::CodeLiteral {
- loc, contract_no, ..
- } => code(loc, *contract_no, ns, opt),
- ast::Expression::NumberLiteral { loc, ty, value } => Expression::NumberLiteral {
- loc: *loc,
- ty: ty.clone(),
- value: value.clone(),
- },
- ast::Expression::RationalNumberLiteral { loc, ty, value } => {
- Expression::RationalNumberLiteral {
- loc: *loc,
- ty: ty.clone(),
- rational: value.clone(),
- }
- }
- ast::Expression::Variable { loc, ty, var_no } => Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: *var_no,
- },
- ast::Expression::List {
- loc,
- list: elements,
- } => Expression::List {
- loc: *loc,
- exprs: elements
- .iter()
- .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>(),
- },
- ast::Expression::GetRef { loc, ty, expr: exp } => Expression::GetRef {
- loc: *loc,
- ty: ty.clone(),
- expr: Box::new(expression(exp, cfg, contract_no, func, ns, vartab, opt)),
- },
- ast::Expression::UserDefinedOperator {
- loc,
- ty,
- function_no,
- args,
- ..
- } => {
- let var = vartab.temp_anonymous(ty);
- let cfg_no = ns.contracts[contract_no].all_functions[function_no];
- let args = args
- .iter()
- .map(|a| expression(a, cfg, contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>();
- cfg.add(
- vartab,
- Instr::Call {
- res: vec![var],
- call: InternalCallTy::Static { cfg_no },
- args,
- return_tys: vec![ty.clone()],
- },
- );
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: var,
- }
- }
- }
- }
- 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 array_pos = match expression(array, cfg, contract_no, func, ns, vartab, opt) {
- Expression::Variable { var_no, .. } => {
- vartab.set_dirty(var_no);
- var_no
- }
- _ => unreachable!(),
- };
- cfg.add(
- vartab,
- Instr::PushMemory {
- res: address_res,
- ty: array.ty(),
- array: array_pos,
- value: Box::new(value),
- },
- );
- cfg.modify_temp_array_length(*loc, false, array_pos, vartab);
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: 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,
- overflowing: 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 {
- loc: var.loc(),
- ty: ty.as_ref().clone(),
- expr: Box::new(v),
- },
- Type::StorageRef(_, ty) => load_storage(&var.loc(), ty.as_ref(), v, cfg, vartab),
- _ => v,
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: v.loc(),
- res,
- expr: v,
- },
- );
- let one = Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: ty.clone(),
- value: BigInt::one(),
- });
- let expr = match expr {
- ast::Expression::PostDecrement { .. } => Expression::Subtract {
- loc: *loc,
- ty: ty.clone(),
- overflowing,
- left: Box::new(Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: res,
- }),
- right: one,
- },
- ast::Expression::PostIncrement { .. } => Expression::Add {
- loc: *loc,
- ty: ty.clone(),
- overflowing,
- left: Box::new(Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: res,
- }),
- right: one,
- },
- _ => unreachable!(),
- };
- match var {
- ast::Expression::Variable { var_no, .. } => {
- cfg.add(
- vartab,
- Instr::Set {
- loc: expr.loc(),
- res: *var_no,
- expr,
- },
- );
- }
- _ => {
- let dest = expression(var, cfg, contract_no, func, ns, vartab, opt);
- let res = vartab.temp_anonymous(ty);
- cfg.add(
- vartab,
- Instr::Set {
- loc: expr.loc(),
- res,
- expr,
- },
- );
- match var.ty() {
- Type::StorageRef(..) => {
- cfg.add(
- vartab,
- Instr::SetStorage {
- value: Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: res,
- },
- ty: ty.clone(),
- storage: dest,
- },
- );
- }
- Type::Ref(_) => {
- cfg.add(
- vartab,
- Instr::Store {
- dest,
- data: Expression::Variable {
- loc: Loc::Codegen,
- ty: ty.clone(),
- var_no: res,
- },
- },
- );
- }
- _ => unreachable!(),
- }
- }
- }
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: 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,
- overflowing: 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 {
- loc: var.loc(),
- ty: ty.as_ref().clone(),
- expr: Box::new(v),
- },
- Type::StorageRef(_, ty) => load_storage(&var.loc(), ty.as_ref(), v, cfg, vartab),
- _ => v,
- };
- let one = Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: ty.clone(),
- value: BigInt::one(),
- });
- let expr = match expr {
- ast::Expression::PreDecrement { .. } => Expression::Subtract {
- loc: *loc,
- ty: ty.clone(),
- overflowing,
- left: Box::new(v),
- right: one,
- },
- ast::Expression::PreIncrement { .. } => Expression::Add {
- loc: *loc,
- ty: ty.clone(),
- overflowing,
- left: Box::new(v),
- right: one,
- },
- _ => unreachable!(),
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: expr.loc(),
- res,
- expr,
- },
- );
- match var {
- ast::Expression::Variable { loc, var_no, .. } => {
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *var_no,
- expr: Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: 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: *loc,
- ty: ty.clone(),
- var_no: res,
- },
- ty: ty.clone(),
- storage: dest,
- },
- );
- }
- Type::Ref(_) => {
- cfg.add(
- vartab,
- Instr::Store {
- dest,
- data: Expression::Variable {
- loc: Loc::Codegen,
- ty: ty.clone(),
- var_no: res,
- },
- },
- );
- }
- _ => unreachable!(),
- }
- }
- }
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: 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);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "or".to_owned(),
- loc: *loc,
- },
- &Type::Bool,
- );
- vartab.new_dirty_tracker();
- 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: *loc,
- res: pos,
- expr: Expression::BoolLiteral {
- loc: *loc,
- value: 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: r.loc(),
- res: pos,
- expr: r,
- },
- );
- cfg.add(vartab, Instr::Branch { block: end_or });
- cfg.set_basic_block(end_or);
- cfg.set_phis(end_or, vartab.pop_dirty_tracker());
- Expression::Variable {
- loc: *loc,
- ty: Type::Bool,
- var_no: 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);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "and".to_owned(),
- loc: *loc,
- },
- &Type::Bool,
- );
- vartab.new_dirty_tracker();
- 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: *loc,
- res: pos,
- expr: Expression::BoolLiteral {
- loc: *loc,
- value: 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: r.loc(),
- res: pos,
- expr: r,
- },
- );
- cfg.add(vartab, Instr::Branch { block: end_and });
- cfg.set_basic_block(end_and);
- cfg.set_phis(end_and, vartab.pop_dirty_tracker());
- Expression::Variable {
- loc: *loc,
- ty: Type::Bool,
- var_no: 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_);
- log_runtime_error(
- opt.log_runtime_errors,
- "assert failure",
- args.loc(),
- cfg,
- vartab,
- ns,
- );
- assert_failure(&Loc::Codegen, None, ns, cfg, vartab);
- 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,
- loc: Loc,
- ) -> 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));
- match ns.target {
- // On Solana and Substrate, print the reason, do not abi encode it
- Target::Solana | Target::Substrate { .. } => {
- if opt.log_runtime_errors {
- if let Some(expr) = expr {
- let prefix = b"runtime_error: ";
- let error_string = format!(
- " require condition failed in {},\n",
- ns.loc_to_string(false, &expr.loc())
- );
- let print_expr = Expression::FormatString {
- loc: Loc::Codegen,
- args: vec![
- (
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(prefix.len() as u8),
- value: prefix.to_vec(),
- },
- ),
- (FormatArg::Default, expr),
- (
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(error_string.as_bytes().len() as u8),
- value: error_string.as_bytes().to_vec(),
- },
- ),
- ],
- };
- cfg.add(vartab, Instr::Print { expr: print_expr });
- } else {
- log_runtime_error(
- opt.log_runtime_errors,
- "require condition failed",
- loc,
- cfg,
- vartab,
- ns,
- );
- }
- }
- assert_failure(&Loc::Codegen, None, ns, cfg, vartab);
- }
- _ => assert_failure(&Loc::Codegen, expr, ns, cfg, vartab),
- }
- cfg.set_basic_block(true_);
- 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::EVM {
- 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,
- seeds: None,
- payload: Expression::AllocDynamicBytes {
- loc: *loc,
- ty: Type::DynamicBytes,
- size: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: BigInt::from(0),
- }),
- initializer: Some(vec![]),
- },
- value,
- gas: Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(64),
- value: BigInt::from(i64::MAX),
- },
- callty: CallTy::Regular,
- contract_function_no: None,
- },
- );
- }
- Expression::Variable {
- loc: *loc,
- ty: Type::Bool,
- var_no: 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::EVM {
- 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,
- seeds: None,
- address: Some(address),
- payload: Expression::AllocDynamicBytes {
- loc: *loc,
- ty: Type::DynamicBytes,
- size: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: BigInt::from(0),
- }),
- initializer: Some(vec![]),
- },
- value,
- gas: Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(64),
- value: BigInt::from(i64::MAX),
- },
- callty: CallTy::Regular,
- contract_function_no: None,
- },
- );
- }
- Expression::Poison
- }
- fn abi_encode_many(
- args: &[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(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>();
- abi_encode(loc, args, ns, vartab, cfg, false).0
- }
- 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 packed = args
- .iter()
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>();
- let (encoded, _) = abi_encode(loc, packed, ns, vartab, cfg, true);
- encoded
- }
- fn encode_many_with_selector(
- loc: &pt::Loc,
- selector: Expression,
- mut args: Vec<Expression>,
- ns: &Namespace,
- vartab: &mut Vartable,
- cfg: &mut ControlFlowGraph,
- ) -> Expression {
- let mut encoder_args: Vec<Expression> = Vec::with_capacity(args.len() + 1);
- encoder_args.push(selector);
- encoder_args.append(&mut args);
- abi_encode(loc, encoder_args, ns, vartab, cfg, false).0
- }
- 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 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::<Vec<Expression>>();
- encode_many_with_selector(loc, selector, args, ns, vartab, cfg)
- }
- 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 args_iter = args.iter();
- let hash_algorithm = if ns.target == Target::Solana {
- ast::Builtin::Sha256
- } else {
- ast::Builtin::Keccak256
- };
- let hash = ast::Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Bytes(32)],
- kind: hash_algorithm,
- args: vec![args_iter.next().unwrap().clone()],
- };
- let hash = expression(&hash, cfg, contract_no, func, ns, vartab, opt);
- let selector = hash.cast(&Type::FunctionSelector, ns);
- let args = args_iter
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>();
- encode_many_with_selector(loc, selector, args, ns, vartab, cfg)
- }
- 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 args_iter = args.iter();
- let selector = expression(
- &ast::Expression::Builtin {
- loc: *loc,
- tys: vec![Type::FunctionSelector],
- kind: ast::Builtin::FunctionSelector,
- args: 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::<Vec<Expression>>();
- encode_many_with_selector(loc, selector, args, ns, vartab, cfg)
- }
- fn builtin_evm_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: *loc,
- tys: vec![ty.clone()],
- kind: Builtin::Gasprice,
- args: vec![],
- };
- let units = expression(&expr[0], cfg, contract_no, func, ns, vartab, opt);
- Expression::Multiply {
- loc: *loc,
- ty,
- overflowing: true,
- left: Box::new(units),
- right: 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: *loc,
- signed: false,
- left: Box::new(Expression::Add {
- loc: *loc,
- ty: Type::Uint(32),
- overflowing: false,
- left: Box::new(offset.clone()),
- right: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: BigInt::from(args[1].ty().bits(ns) / 8),
- }),
- }),
- right: Box::new(Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Uint(32)],
- kind: Builtin::ArrayLength,
- args: 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);
- log_runtime_error(
- opt.log_runtime_errors,
- "integer too large to write in buffer",
- *loc,
- cfg,
- vartab,
- ns,
- );
- assert_failure(loc, None, ns, cfg, vartab);
- 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 { ty: tys[0].clone() }
- }
- ast::Builtin::WriteBytes | ast::Builtin::WriteString => {
- let buffer = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let data = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
- let offset = expression(&args[2], cfg, contract_no, func, ns, vartab, opt);
- let size = Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Uint(32)],
- kind: Builtin::ArrayLength,
- args: vec![data.clone()],
- };
- let cond = Expression::LessEqual {
- loc: *loc,
- signed: false,
- left: Box::new(Expression::Add {
- loc: *loc,
- ty: Type::Uint(32),
- overflowing: false,
- left: Box::new(offset.clone()),
- right: Box::new(size.clone()),
- }),
- right: Box::new(Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Uint(32)],
- kind: Builtin::ArrayLength,
- args: vec![buffer.clone()],
- }),
- };
- let in_bounds = cfg.new_basic_block("in_bounds".to_string());
- let out_ouf_bounds = cfg.new_basic_block("out_of_bounds".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: in_bounds,
- false_block: out_ouf_bounds,
- },
- );
- cfg.set_basic_block(out_ouf_bounds);
- log_runtime_error(
- opt.log_runtime_errors,
- "data does not fit into buffer",
- *loc,
- cfg,
- vartab,
- ns,
- );
- assert_failure(loc, None, ns, cfg, vartab);
- cfg.set_basic_block(in_bounds);
- let advanced_ptr = Expression::AdvancePointer {
- pointer: Box::new(buffer),
- bytes_offset: Box::new(offset),
- };
- cfg.add(
- vartab,
- Instr::MemCopy {
- source: data,
- destination: advanced_ptr,
- bytes: size,
- },
- );
- Expression::Undefined { ty: 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: *loc,
- signed: false,
- left: Box::new(Expression::Add {
- loc: *loc,
- ty: Type::Uint(32),
- overflowing: false,
- left: Box::new(offset.clone()),
- right: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: BigInt::from(tys[0].bits(ns) / 8),
- }),
- }),
- right: Box::new(Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Uint(32)],
- kind: Builtin::ArrayLength,
- args: 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);
- log_runtime_error(
- opt.log_runtime_errors,
- "read integer out of bounds",
- *loc,
- cfg,
- vartab,
- ns,
- );
- assert_failure(loc, None, ns, cfg, vartab);
- cfg.set_basic_block(in_bounds);
- Expression::Builtin {
- loc: *loc,
- tys: tys.to_vec(),
- kind: (&builtin).into(),
- args: vec![buf, offset],
- }
- }
- ast::Builtin::AddMod | ast::Builtin::MulMod => {
- let arguments: Vec<Expression> = args
- .iter()
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- let temp = vartab.temp_anonymous(&tys[0]);
- let zero = Expression::NumberLiteral {
- loc: *loc,
- ty: tys[0].clone(),
- value: BigInt::zero(),
- };
- let cond = Expression::NotEqual {
- loc: *loc,
- left: Box::new(zero.clone()),
- right: Box::new(arguments[2].clone()),
- };
- let true_block = cfg.new_basic_block("builtin_call".to_string());
- let false_block = cfg.new_basic_block("zero".to_string());
- let end_if = cfg.new_basic_block("end_if".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block,
- false_block,
- },
- );
- cfg.set_basic_block(true_block);
- vartab.new_dirty_tracker();
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: temp,
- expr: Expression::Builtin {
- loc: *loc,
- tys: tys.to_vec(),
- kind: (&builtin).into(),
- args: arguments,
- },
- },
- );
- cfg.add(vartab, Instr::Branch { block: end_if });
- cfg.set_basic_block(false_block);
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: temp,
- expr: zero,
- },
- );
- cfg.add(vartab, Instr::Branch { block: end_if });
- cfg.set_phis(end_if, vartab.pop_dirty_tracker());
- cfg.set_basic_block(end_if);
- Expression::Variable {
- loc: *loc,
- ty: tys[0].clone(),
- var_no: temp,
- }
- }
- _ => {
- let arguments: Vec<Expression> = args
- .iter()
- .map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
- .collect();
- if !arguments.is_empty() && builtin == ast::Builtin::ArrayLength {
- // If an array length instruction is called
- // Get the variable it is assigned with
- if let Expression::Variable { var_no, .. } = &arguments[0] {
- // Now that we have its temp in the map, retrieve the temp var res from the map
- if let Some(array_length_var) = cfg.array_lengths_temps.get(var_no) {
- // If it's there, replace ArrayLength with the temp var
- return Expression::Variable {
- loc: *loc,
- ty: Type::Uint(32),
- var_no: *array_length_var,
- };
- }
- }
- }
- Expression::Builtin {
- loc: *loc,
- tys: tys.to_vec(),
- kind: (&builtin).into(),
- args: arguments,
- }
- }
- }
- }
- 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::AllocDynamicBytes {
- loc: *loc,
- ty: ty.clone(),
- size: Box::new(size),
- initializer: init.clone(),
- }
- }
- fn add(
- loc: &pt::Loc,
- ty: &Type,
- overflowing: 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: *loc,
- ty: ty.clone(),
- overflowing,
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
- }
- }
- fn subtract(
- loc: &pt::Loc,
- ty: &Type,
- overflowing: 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: *loc,
- ty: ty.clone(),
- overflowing,
- left: Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
- right: 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: *loc,
- ty: source_ty.clone(),
- value: 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: expr.loc(),
- 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: *loc,
- signed: false,
- left: Box::new(Expression::Variable {
- loc: *loc,
- ty: source_ty.clone(),
- var_no: pos,
- }),
- right: Box::new(overflow),
- },
- true_block: out_of_bounds,
- false_block: in_bounds,
- },
- );
- cfg.set_basic_block(out_of_bounds);
- log_runtime_error(
- opt.log_runtime_errors,
- "truncated type overflows",
- *loc,
- cfg,
- vartab,
- ns,
- );
- assert_failure(loc, None, ns, cfg, vartab);
- cfg.set_basic_block(in_bounds);
- Expression::Trunc {
- loc: *loc,
- ty: ty.clone(),
- expr: Box::new(Expression::Variable {
- loc: *loc,
- ty: source_ty,
- var_no: 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: *loc, args }
- }
- fn conditional_operator(
- 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);
- let pos = vartab.temp(
- &pt::Identifier {
- name: "ternary_result".to_owned(),
- loc: *loc,
- },
- ty,
- );
- vartab.new_dirty_tracker();
- 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("conditional_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: expr.loc(),
- 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: expr.loc(),
- res: pos,
- expr,
- },
- );
- cfg.add(vartab, Instr::Branch { block: done_block });
- cfg.set_basic_block(done_block);
- cfg.set_phis(done_block, vartab.pop_dirty_tracker());
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: pos,
- }
- }
- fn interfaceid(ns: &Namespace, contract_no: usize, loc: &pt::Loc) -> Expression {
- let selector_len = ns.target.selector_length();
- let mut id = vec![0u8; selector_len as usize];
- for func_no in &ns.contracts[contract_no].functions {
- let func = &ns.functions[*func_no];
- if func.ty == pt::FunctionTy::Function {
- let selector = func.selector(ns, &contract_no);
- debug_assert_eq!(id.len(), selector.len());
- for (i, e) in id.iter_mut().enumerate() {
- *e ^= selector[i];
- }
- }
- }
- Expression::BytesLiteral {
- loc: *loc,
- ty: Type::Bytes(selector_len),
- value: id.to_vec(),
- }
- }
- 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, var_no } => {
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *var_no,
- expr: cfg_right,
- },
- );
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: *var_no,
- }
- }
- _ => {
- 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(ns) {
- Expression::Load {
- loc: pt::Loc::Codegen,
- ty: cfg_right.ty(),
- expr: 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 {
- expr: array, index, ..
- } = dest
- {
- // Set a byte in a byte array
- cfg.add(
- vartab,
- Instr::SetStorageBytes {
- value: Expression::Variable {
- loc: left.loc(),
- ty: ty.clone(),
- var_no: pos,
- },
- storage: *array,
- offset: *index,
- },
- );
- } else {
- unreachable!();
- }
- }
- Type::StorageRef(..) => {
- cfg.add(
- vartab,
- Instr::SetStorage {
- value: Expression::Variable {
- loc: left.loc(),
- ty: ty.clone(),
- var_no: pos,
- },
- ty: ty.deref_any().clone(),
- storage: dest,
- },
- );
- }
- Type::Ref(_) => {
- cfg.add(
- vartab,
- Instr::Store {
- dest,
- data: Expression::Variable {
- loc: Loc::Codegen,
- ty: ty.clone(),
- var_no: pos,
- },
- },
- );
- }
- _ => unreachable!(),
- }
- Expression::Variable {
- loc: left.loc(),
- ty: ty.clone(),
- var_no: pos,
- }
- }
- }
- }
- /// Convert a function call expression to CFG in expression context
- pub fn emit_function_call(
- expr: &ast::Expression,
- caller_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, caller_contract_no, func, ns, vartab, opt))
- .collect();
- let function_no = if let Some(signature) = signature {
- ns.contracts[caller_contract_no].virtual_functions[signature]
- } else {
- *function_no
- };
- let ftype = &ns.functions[function_no];
- let call = if ns.functions[function_no].loc == pt::Loc::Builtin {
- InternalCallTy::Builtin {
- ast_func_no: function_no,
- }
- } else {
- let cfg_no = ns.contracts[caller_contract_no].all_functions[&function_no];
- InternalCallTy::Static { cfg_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 {
- loc: id.loc,
- ty: ret.ty.clone(),
- var_no: temp_pos,
- });
- }
- cfg.add(
- vartab,
- Instr::Call {
- res,
- call,
- args,
- return_tys,
- },
- );
- returns
- } else {
- cfg.add(
- vartab,
- Instr::Call {
- res: Vec::new(),
- return_tys: Vec::new(),
- call,
- args,
- },
- );
- vec![Expression::Poison]
- }
- } else if let Type::InternalFunction { returns, .. } = function.ty().deref_any() {
- let cfg_expr = expression(function, cfg, caller_contract_no, func, ns, vartab, opt);
- let args = args
- .iter()
- .map(|a| expression(a, cfg, caller_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 {
- loc: id.loc,
- ty: ty.clone(),
- var_no: 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, caller_contract_no, func, ns, vartab, opt);
- let address = expression(address, cfg, caller_contract_no, func, ns, vartab, opt);
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, caller_contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, caller_contract_no, func, ns, vartab, opt)
- } else {
- Expression::NumberLiteral {
- loc: pt::Loc::Codegen,
- ty: Type::Value,
- value: BigInt::zero(),
- }
- };
- let accounts = call_args
- .accounts
- .as_ref()
- .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
- let seeds = call_args
- .seeds
- .as_ref()
- .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
- let success = vartab.temp_name("success", &Type::Bool);
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: Some(success),
- address: Some(address),
- payload: args,
- value,
- accounts,
- seeds,
- gas,
- callty: ty.clone(),
- contract_function_no: None,
- },
- );
- vec![
- Expression::Variable {
- loc: *loc,
- ty: Type::Bool,
- var_no: success,
- },
- Expression::ReturnData { loc: *loc },
- ]
- }
- ast::Expression::ExternalFunctionCall {
- loc,
- function,
- args,
- returns,
- call_args,
- ..
- } => {
- if let ast::Expression::ExternalFunction {
- function_no,
- address,
- ..
- } = function.as_ref()
- {
- let dest_func = &ns.functions[*function_no];
- let contract_function_no = dest_func
- .contract_no
- .map(|contract_no| (contract_no, *function_no));
- let mut tys: Vec<Type> = args.iter().map(|a| a.ty()).collect();
- let mut args: Vec<Expression> = args
- .iter()
- .map(|a| expression(a, cfg, caller_contract_no, func, ns, vartab, opt))
- .collect();
- let address = expression(address, cfg, caller_contract_no, func, ns, vartab, opt);
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, caller_contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let accounts = call_args
- .accounts
- .as_ref()
- .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
- let seeds = call_args
- .seeds
- .as_ref()
- .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, caller_contract_no, func, ns, vartab, opt)
- } else {
- Expression::NumberLiteral {
- loc: pt::Loc::Codegen,
- ty: Type::Value,
- value: BigInt::zero(),
- }
- };
- let selector = dest_func.selector(ns, &caller_contract_no);
- tys.insert(0, Type::Bytes(selector.len() as u8));
- args.insert(
- 0,
- Expression::BytesLiteral {
- loc: *loc,
- ty: Type::Bytes(selector.len() as u8),
- value: selector,
- },
- );
- let (payload, _) = abi_encode(loc, args, ns, vartab, cfg, false);
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: None,
- accounts,
- address: Some(address),
- payload,
- seeds,
- value,
- gas,
- callty: CallTy::Regular,
- contract_function_no,
- },
- );
- // If the first element of returns is Void, we can discard the returns
- if !dest_func.returns.is_empty() && returns[0] != Type::Void {
- let tys = dest_func
- .returns
- .iter()
- .map(|e| e.ty.clone())
- .collect::<Vec<Type>>();
- abi_decode(
- loc,
- &Expression::ReturnData { loc: *loc },
- &tys,
- ns,
- vartab,
- cfg,
- None,
- )
- } 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 mut args = args
- .iter()
- .map(|a| expression(a, cfg, caller_contract_no, func, ns, vartab, opt))
- .collect::<Vec<Expression>>();
- let function = expression(function, cfg, caller_contract_no, func, ns, vartab, opt);
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, caller_contract_no, func, ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, caller_contract_no, func, ns, vartab, opt)
- } else {
- Expression::NumberLiteral {
- loc: pt::Loc::Codegen,
- ty: Type::Value,
- value: BigInt::zero(),
- }
- };
- let selector = function.external_function_selector();
- let address = function.external_function_address();
- tys.insert(0, Type::Bytes(ns.target.selector_length()));
- args.insert(0, selector);
- let (payload, _) = abi_encode(loc, args, ns, vartab, cfg, false);
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: None,
- accounts: None,
- seeds: None,
- address: Some(address),
- payload,
- value,
- gas,
- callty: CallTy::Regular,
- contract_function_no: None,
- },
- );
- if !func_returns.is_empty() && returns[0] != Type::Void {
- abi_decode(
- loc,
- &Expression::ReturnData { loc: *loc },
- returns,
- ns,
- vartab,
- cfg,
- None,
- )
- } else {
- vec![Expression::Poison]
- }
- } else {
- unreachable!();
- }
- }
- ast::Expression::Builtin {
- loc,
- tys,
- kind: ast::Builtin::AbiDecode,
- args,
- } => {
- let data = expression(&args[0], cfg, caller_contract_no, func, ns, vartab, opt);
- abi_decode(loc, &data, tys, ns, vartab, cfg, None)
- }
- _ => unreachable!(),
- }
- }
- pub fn default_gas(ns: &Namespace) -> Expression {
- Expression::NumberLiteral {
- loc: pt::Loc::Codegen,
- ty: Type::Uint(64),
- // See EIP150
- value: if ns.target == Target::EVM {
- 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: *loc,
- ty: elem_ty.clone(),
- array_ty: array_ty.clone(),
- expr: Box::new(expression(array, cfg, contract_no, func, ns, vartab, opt)),
- index: 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: *loc,
- ty: elem_ty.clone(),
- array_ty: array_ty.clone(),
- expr: Box::new(array),
- index: Box::new(index),
- }
- } else {
- Expression::Keccak256 {
- loc: *loc,
- ty: array_ty.clone(),
- exprs: 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 Diagnostics::default(),
- ResolveTo::Unknown,
- None,
- )
- .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: *loc,
- ty: Type::Uint(256),
- exprs: vec![array],
- };
- array_length
- }
- } else {
- // If a subscript is encountered array length will be called
- // Return array length by default
- let mut returned = Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Uint(32)],
- kind: Builtin::ArrayLength,
- args: vec![array.clone()],
- };
- if let Expression::Variable {
- loc, var_no: num, ..
- } = &array
- {
- // If the size is known (is in cfg.array_length_map), do the replacement
- if let Some(array_length_var) = cfg.array_lengths_temps.get(num) {
- returned = Expression::Variable {
- loc: *loc,
- ty: Type::Uint(32),
- var_no: *array_length_var,
- };
- }
- }
- returned
- }
- }
- Some(l) => {
- let ast_big_int = bigint_to_expression(
- loc,
- l,
- ns,
- &mut Diagnostics::default(),
- ResolveTo::Unknown,
- None,
- )
- .unwrap();
- expression(&ast_big_int, cfg, contract_no, func, ns, vartab, opt)
- }
- },
- Type::DynamicBytes => Expression::Builtin {
- loc: *loc,
- tys: vec![Type::Uint(32)],
- kind: Builtin::ArrayLength,
- args: 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,
- );
- let expr = index.cast(&coerced_ty, ns);
- cfg.add(
- vartab,
- Instr::Set {
- loc: expr.loc(),
- res: pos,
- expr,
- },
- );
- // 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: *loc,
- signed: false,
- left: Box::new(Expression::Variable {
- loc: index_loc,
- ty: coerced_ty.clone(),
- var_no: pos,
- }),
- right: Box::new(array_length.cast(&coerced_ty, ns)),
- },
- true_block: out_of_bounds,
- false_block: in_bounds,
- },
- );
- cfg.set_basic_block(out_of_bounds);
- log_runtime_error(
- opt.log_runtime_errors,
- "array index out of bounds",
- *loc,
- cfg,
- vartab,
- ns,
- );
- assert_failure(loc, None, ns, cfg, vartab);
- cfg.set_basic_block(in_bounds);
- if let Type::Bytes(array_length) = array_ty.deref_any() {
- 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 {
- loc: index_loc,
- ty: coerced_ty.clone(),
- var_no: pos,
- };
- let shift_arg = match index_width.cmp(&to_width) {
- Ordering::Equal => shift_arg_raw,
- Ordering::Less => Expression::ZeroExt {
- loc: *loc,
- ty: index_ty.clone(),
- expr: shift_arg_raw.into(),
- },
- Ordering::Greater => Expression::Trunc {
- loc: *loc,
- ty: index_ty.clone(),
- expr: shift_arg_raw.into(),
- },
- };
- return Expression::Trunc {
- loc: *loc,
- ty: res_ty,
- expr: Expression::ShiftRight {
- loc: *loc,
- ty: from_ty,
- left: array.into(),
- right: Expression::ShiftLeft {
- loc: *loc,
- ty: index_ty.clone(),
- left: Box::new(Expression::Subtract {
- loc: *loc,
- ty: index_ty.clone(),
- overflowing: true,
- left: Expression::NumberLiteral {
- loc: *loc,
- ty: index_ty.clone(),
- value: BigInt::from_u8(array_length - 1).unwrap(),
- }
- .into(),
- right: shift_arg.into(),
- }),
- right: Expression::NumberLiteral {
- loc: *loc,
- ty: index_ty,
- value: BigInt::from_u8(3).unwrap(),
- }
- .into(),
- }
- .into(),
- signed: false,
- }
- .into(),
- };
- }
- 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 {
- loc: index_loc,
- ty: coerced_ty,
- var_no: pos,
- }
- .cast(&Type::Uint(256), ns);
- Expression::Subscript {
- loc: *loc,
- ty: elem_ty,
- array_ty: array_ty.clone(),
- expr: Box::new(array),
- index: Box::new(index),
- }
- } else {
- let index = Expression::Variable {
- loc: index_loc,
- ty: coerced_ty,
- var_no: pos,
- }
- .cast(&slot_ty, ns);
- if ty.array_length().is_some() {
- // fixed length array
- let elem_size = elem_ty.deref_any().solana_storage_size(ns);
- Expression::Add {
- loc: *loc,
- ty: elem_ty,
- overflowing: true,
- left: Box::new(array),
- right: Box::new(Expression::Multiply {
- loc: *loc,
- ty: slot_ty.clone(),
- overflowing: true,
- left: Box::new(index),
- right: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: slot_ty,
- value: elem_size,
- }),
- }),
- }
- } else {
- Expression::Subscript {
- loc: *loc,
- ty: elem_ty,
- array_ty: array_ty.clone(),
- expr: Box::new(array),
- index: Box::new(index),
- }
- }
- }
- } else {
- let elem_size = elem_ty.storage_slots(ns);
- if let Expression::NumberLiteral {
- value: 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: *loc,
- ty: elem_ty,
- overflowing: true,
- left: Box::new(array),
- right: Box::new(Expression::ZeroExt {
- loc: *loc,
- ty: slot_ty,
- expr: Box::new(Expression::Multiply {
- loc: *loc,
- ty: Type::Uint(64),
- overflowing: true,
- left: Box::new(
- Expression::Variable {
- loc: index_loc,
- ty: coerced_ty,
- var_no: pos,
- }
- .cast(&Type::Uint(64), ns),
- ),
- right: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(64),
- value: elem_size,
- }),
- }),
- }),
- };
- }
- }
- array_offset(
- loc,
- array,
- Expression::Variable {
- loc: index_loc,
- ty: coerced_ty,
- var_no: pos,
- }
- .cast(&ns.storage_type(), ns),
- elem_ty,
- ns,
- )
- }
- } else {
- match array_ty.deref_memory() {
- Type::DynamicBytes | Type::Array(..) => Expression::Subscript {
- loc: *loc,
- ty: elem_ty.clone(),
- array_ty: array_ty.clone(),
- expr: Box::new(array),
- index: Box::new(Expression::Variable {
- loc: index_loc,
- ty: coerced_ty,
- var_no: 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: *loc,
- ty: ty.clone(),
- var_no: res,
- }
- }
- fn array_literal_to_memory_array(
- loc: &pt::Loc,
- expr: &Expression,
- ty: &Type,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ) -> Expression {
- let memory_array = vartab.temp_anonymous(ty);
- let elem_ty = ty.array_elem();
- let dims = expr.ty().array_length().unwrap().clone();
- let array_size = Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: dims,
- };
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: memory_array,
- expr: Expression::AllocDynamicBytes {
- loc: *loc,
- ty: ty.clone(),
- size: Box::new(array_size.clone()),
- initializer: None,
- },
- },
- );
- let elements = if let Expression::ArrayLiteral { values: items, .. } = expr {
- items
- } else {
- unreachable!()
- };
- for (item_no, item) in elements.iter().enumerate() {
- cfg.add(
- vartab,
- Instr::Store {
- dest: Expression::Subscript {
- loc: *loc,
- ty: Type::Ref(Box::new(elem_ty.clone())),
- array_ty: ty.clone(),
- expr: Box::new(Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: memory_array,
- }),
- index: Box::new(Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: BigInt::from(item_no),
- }),
- },
- data: item.clone(),
- },
- );
- }
- let temp_res = vartab.temp_name("array_length", &Type::Uint(32));
- cfg.add(
- vartab,
- Instr::Set {
- loc: Loc::Codegen,
- res: temp_res,
- expr: array_size,
- },
- );
- cfg.array_lengths_temps.insert(memory_array, temp_res);
- Expression::Variable {
- loc: *loc,
- ty: ty.clone(),
- var_no: memory_array,
- }
- }
- /// This function encodes the arguments for the assert-failure instruction
- /// and inserts it in the CFG.
- pub(super) fn assert_failure(
- loc: &Loc,
- arg: Option<Expression>,
- ns: &Namespace,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ) {
- if arg.is_none() {
- cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
- return;
- }
- let selector = 0x08c3_79a0u32;
- let selector = Expression::NumberLiteral {
- loc: Loc::Codegen,
- ty: Type::Uint(32),
- value: BigInt::from(selector),
- };
- let args = vec![selector, arg.unwrap()];
- let (encoded_buffer, _) = abi_encode(loc, args, ns, vartab, cfg, false);
- cfg.add(
- vartab,
- Instr::AssertFailure {
- encoded_args: Some(encoded_buffer),
- },
- )
- }
- /// Generate the binary code for a contract
- fn code(loc: &Loc, contract_no: usize, ns: &Namespace, opt: &Options) -> Expression {
- let contract = &ns.contracts[contract_no];
- let code = contract.emit(ns, opt);
- let size = Expression::NumberLiteral {
- loc: *loc,
- ty: Type::Uint(32),
- value: code.len().into(),
- };
- Expression::AllocDynamicBytes {
- loc: *loc,
- ty: Type::DynamicBytes,
- size: size.into(),
- initializer: Some(code),
- }
- }
- fn string_to_expr(string: String) -> Expression {
- Expression::FormatString {
- loc: Loc::Codegen,
- args: vec![(
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(string.as_bytes().len() as u8),
- value: string.as_bytes().to_vec(),
- },
- )],
- }
- }
- pub(crate) fn log_runtime_error(
- report_error: bool,
- reason: &str,
- reason_loc: Loc,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ns: &Namespace,
- ) {
- if report_error {
- let error_with_loc = error_msg_with_loc(ns, reason, Some(reason_loc));
- let expr = string_to_expr(error_with_loc + ",\n");
- cfg.add(vartab, Instr::Print { expr });
- }
- }
- fn add_prefix_and_delimiter_to_print(mut expr: Expression) -> Expression {
- let prefix = b"print: ";
- let delimiter = b",\n";
- if let Expression::FormatString { loc, args } = &mut expr {
- let mut new_vec = Vec::new();
- new_vec.push((
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(prefix.len() as u8),
- value: prefix.to_vec(),
- },
- ));
- new_vec.append(args);
- new_vec.push((
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(delimiter.len() as u8),
- value: delimiter.to_vec(),
- },
- ));
- Expression::FormatString {
- loc: *loc,
- args: new_vec,
- }
- } else {
- Expression::FormatString {
- loc: Loc::Codegen,
- args: vec![
- (
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(prefix.len() as u8),
- value: prefix.to_vec(),
- },
- ),
- (FormatArg::Default, expr),
- (
- FormatArg::StringLiteral,
- Expression::BytesLiteral {
- loc: Loc::Codegen,
- ty: Type::Bytes(delimiter.len() as u8),
- value: delimiter.to_vec(),
- },
- ),
- ],
- }
- }
- }
|