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::encoding::create_encoder;
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::{bigint_to_expression, ResolveTo},
};
use crate::Target;
use num_bigint::BigInt;
use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
use solang_parser::pt;
use solang_parser::pt::{CodeLocation, Loc};
use std::ops::Mul;
pub fn expression(
expr: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
opt: &Options,
) -> Expression {
match expr {
ast::Expression::StorageVariable(loc, _, var_contract_no, var_no) => {
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,
} => substract(
loc,
ty,
unchecked,
left,
cfg,
contract_no,
func,
ns,
vartab,
right,
opt,
),
ast::Expression::Multiply {
loc,
ty,
unchecked,
left,
right,
} => {
if ty.is_rational() {
let (_, r) = eval_const_rational(expr, ns).unwrap();
Expression::NumberLiteral(*loc, ty.clone(), r.to_integer())
} else {
Expression::Multiply(
*loc,
ty.clone(),
*unchecked,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
)
}
}
ast::Expression::Divide {
loc,
ty,
left,
right,
} => {
let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
if ty.is_signed_int() {
Expression::SignedDivide(*loc, ty.clone(), Box::new(l), Box::new(r))
} else {
Expression::UnsignedDivide(*loc, ty.clone(), Box::new(l), Box::new(r))
}
}
ast::Expression::Modulo {
loc,
ty,
left,
right,
} => {
let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
if ty.is_signed_int() {
Expression::SignedModulo(*loc, ty.clone(), Box::new(l), Box::new(r))
} else {
Expression::UnsignedModulo(*loc, ty.clone(), Box::new(l), Box::new(r))
}
}
ast::Expression::Power {
loc,
ty,
unchecked,
left,
right,
} => Expression::Power(
*loc,
ty.clone(),
*unchecked,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::BitwiseOr {
loc,
ty,
left,
right,
} => Expression::BitwiseOr(
*loc,
ty.clone(),
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::BitwiseAnd {
loc,
ty,
left,
right,
} => Expression::BitwiseAnd(
*loc,
ty.clone(),
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::BitwiseXor {
loc,
ty,
left,
right,
} => Expression::BitwiseXor(
*loc,
ty.clone(),
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::ShiftLeft {
loc,
ty,
left,
right,
} => Expression::ShiftLeft(
*loc,
ty.clone(),
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::ShiftRight {
loc,
ty,
left,
right,
sign,
} => Expression::ShiftRight(
*loc,
ty.clone(),
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
*sign,
),
ast::Expression::Equal(loc, left, right) => Expression::Equal(
*loc,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::NotEqual(loc, left, right) => Expression::NotEqual(
*loc,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::More(loc, left, right) => {
let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
if l.ty().is_signed_int() {
Expression::SignedMore(*loc, Box::new(l), Box::new(r))
} else {
Expression::UnsignedMore(*loc, Box::new(l), Box::new(r))
}
}
ast::Expression::MoreEqual(loc, left, right) => Expression::MoreEqual(
*loc,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::Less(loc, left, right) => {
let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
if l.ty().is_signed_int() {
Expression::SignedLess(*loc, Box::new(l), Box::new(r))
} else {
Expression::UnsignedLess(*loc, Box::new(l), Box::new(r))
}
}
ast::Expression::LessEqual(loc, left, right) => Expression::LessEqual(
*loc,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::ConstantVariable(_, _, Some(var_contract_no), var_no) => expression(
ns.contracts[*var_contract_no].variables[*var_no]
.initializer
.as_ref()
.unwrap(),
cfg,
contract_no,
func,
ns,
vartab,
opt,
),
ast::Expression::ConstantVariable(_, _, None, var_no) => expression(
ns.constants[*var_no].initializer.as_ref().unwrap(),
cfg,
contract_no,
func,
ns,
vartab,
opt,
),
ast::Expression::Not(loc, expr) => Expression::Not(
*loc,
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::Complement(loc, ty, expr) => Expression::Complement(
*loc,
ty.clone(),
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::UnaryMinus(loc, ty, expr) => Expression::UnaryMinus(
*loc,
ty.clone(),
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::StructLiteral(loc, ty, exprs) => Expression::StructLiteral(
*loc,
ty.clone(),
exprs
.iter()
.map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
.collect(),
),
ast::Expression::ArrayLiteral(loc, ty, lengths, args) => Expression::ArrayLiteral(
*loc,
ty.clone(),
lengths.clone(),
args.iter()
.map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
.collect(),
),
ast::Expression::ConstArrayLiteral(loc, ty, lengths, args) => {
Expression::ConstArrayLiteral(
*loc,
ty.clone(),
lengths.clone(),
args.iter()
.map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
.collect(),
)
}
ast::Expression::Assign(_, _, left, right) => {
if 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 let ast::Expression::Variable(_, Type::Array(..), pos) = &**left {
cfg_right = handle_array_assign(cfg_right, cfg, vartab, pos);
}
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_no,
args,
call_args,
} => {
let address_res = vartab.temp_anonymous(&Type::Contract(*contract_no));
call_constructor(
loc,
contract_no,
*contract_no,
constructor_no,
args,
call_args,
address_res,
None,
func,
ns,
vartab,
cfg,
opt,
);
Expression::Variable(*loc, Type::Contract(*contract_no), address_res)
}
ast::Expression::InternalFunction {
function_no,
signature,
..
} => {
let function_no = if let Some(signature) = signature {
&ns.contracts[contract_no].virtual_functions[signature]
} else {
function_no
};
Expression::InternalFunctionCfg(ns.contracts[contract_no].all_functions[function_no])
}
ast::Expression::StorageArrayLength {
loc,
ty,
array,
elem_ty,
} => {
let array_ty = array.ty().deref_into();
let array = expression(array, cfg, contract_no, func, ns, vartab, opt);
match array_ty {
Type::Bytes(length) => {
let ast_expr = bigint_to_expression(
loc,
&BigInt::from_u8(length).unwrap(),
ns,
&mut Diagnostics::default(),
ResolveTo::Type(ty),
)
.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),
)
.unwrap();
expression(&ast_expr, cfg, contract_no, func, ns, vartab, opt)
}
_ => unreachable!(),
},
_ => unreachable!(),
}
}
ast::Expression::Builtin(_, _, ast::Builtin::ExternalFunctionAddress, func_expr) => {
if let ast::Expression::ExternalFunction { address, .. } = &func_expr[0] {
expression(address, cfg, contract_no, func, ns, vartab, opt)
} else {
let func_expr = expression(&func_expr[0], cfg, contract_no, func, ns, vartab, opt);
func_expr.external_function_address()
}
}
ast::Expression::Builtin(loc, _, ast::Builtin::FunctionSelector, func_expr) => {
match &func_expr[0] {
ast::Expression::ExternalFunction { function_no, .. }
| ast::Expression::InternalFunction { function_no, .. } => {
let selector = ns.functions[*function_no].selector(ns, &contract_no);
Expression::BytesLiteral(*loc, Type::Bytes(selector.len() as u8), 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(_, _, 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,
Type::Uint(32),
ns.functions[*function_no].selector(ns, &contract_no),
);
let struct_literal = Expression::StructLiteral(
*loc,
Type::Struct(StructType::ExternalFunction),
vec![selector, address],
);
Expression::Cast(*loc, ty.clone(), Box::new(struct_literal))
}
ast::Expression::Subscript(loc, elem_ty, array_ty, array, index) => array_subscript(
loc,
elem_ty,
array_ty,
array,
index,
cfg,
contract_no,
func,
ns,
vartab,
opt,
),
ast::Expression::StructMember(loc, ty, var, field_no) if ty.is_contract_storage() => {
if let Type::Struct(struct_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()
.map(|field| field.ty.storage_slots(ns))
.sum()
};
Expression::Add(
*loc,
ns.storage_type(),
true,
Box::new(expression(var, cfg, contract_no, func, ns, vartab, opt)),
Box::new(Expression::NumberLiteral(*loc, ns.storage_type(), offset)),
)
} else {
unreachable!();
}
}
ast::Expression::StructMember(loc, ty, var, member) => Expression::StructMember(
*loc,
ty.clone(),
Box::new(expression(var, cfg, contract_no, func, ns, vartab, opt)),
*member,
),
ast::Expression::StringCompare(loc, left, right) => Expression::StringCompare(
*loc,
string_location(left, cfg, contract_no, func, ns, vartab, opt),
string_location(right, cfg, contract_no, func, ns, vartab, opt),
),
ast::Expression::StringConcat(loc, ty, left, right) => Expression::StringConcat(
*loc,
ty.clone(),
string_location(left, cfg, contract_no, func, ns, vartab, opt),
string_location(right, cfg, contract_no, func, ns, vartab, opt),
),
ast::Expression::Or(loc, left, right) => {
expr_or(left, cfg, contract_no, func, ns, vartab, loc, right, opt)
}
ast::Expression::And(loc, left, right) => {
and(left, cfg, contract_no, func, ns, vartab, loc, right, opt)
}
ast::Expression::CheckingTrunc { loc, to, expr } => {
checking_trunc(loc, expr, to, cfg, contract_no, func, ns, vartab, opt)
}
ast::Expression::Trunc { loc, to, expr } => Expression::Trunc(
*loc,
to.clone(),
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::ZeroExt { loc, to, expr } => Expression::ZeroExt(
*loc,
to.clone(),
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::SignExt { loc, to, expr } => Expression::SignExt(
*loc,
to.clone(),
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, to.clone(), address)
} else {
Expression::Cast(
*loc,
to.clone(),
Box::new(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, to.clone(), 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,
to.clone(),
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
)
}
}
ast::Expression::BytesCast {
loc,
to,
from,
expr,
} => Expression::BytesCast(
*loc,
to.clone(),
from.clone(),
Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::Load(loc, ty, e) => Expression::Load(
*loc,
ty.clone(),
Box::new(expression(e, cfg, contract_no, func, ns, vartab, opt)),
),
ast::Expression::Builtin(_, _, ast::Builtin::UserTypeWrap, args)
| ast::Expression::Builtin(_, _, ast::Builtin::UserTypeUnwrap, args) => {
expression(&args[0], cfg, contract_no, func, ns, vartab, opt)
}
ast::Expression::Builtin(loc, ty, ast::Builtin::ArrayPush, args) => {
if args[0].ty().is_contract_storage() {
if ns.target == Target::Solana || args[0].ty().is_storage_bytes() {
array_push(loc, args, cfg, contract_no, func, ns, vartab, opt)
} else {
storage_slots_array_push(loc, args, cfg, contract_no, func, ns, vartab, opt)
}
} else {
let second_arg = if args.len() > 1 {
expression(&args[1], cfg, contract_no, func, ns, vartab, opt)
} else {
ty[0].default(ns).unwrap()
};
memory_array_push(
&ty[0],
vartab,
&args[0],
cfg,
contract_no,
func,
ns,
second_arg,
loc,
opt,
)
}
}
ast::Expression::Builtin(loc, ty, ast::Builtin::ArrayPop, args) => {
if args[0].ty().is_contract_storage() {
if ns.target == Target::Solana || args[0].ty().is_storage_bytes() {
array_pop(loc, args, &ty[0], cfg, contract_no, func, ns, vartab, opt)
} else {
storage_slots_array_pop(
loc,
args,
&ty[0],
cfg,
contract_no,
func,
ns,
vartab,
opt,
)
}
} else {
let address_res = vartab.temp_anonymous(&ty[0]);
let array_pos = match expression(&args[0], cfg, contract_no, func, ns, vartab, opt)
{
Expression::Variable(_, _, pos) => {
vartab.set_dirty(pos);
pos
}
_ => unreachable!(),
};
cfg.add(
vartab,
Instr::PopMemory {
res: address_res,
ty: args[0].ty(),
array: array_pos,
},
);
cfg.modify_temp_array_length(*loc, true, array_pos, vartab);
Expression::Variable(*loc, ty[0].clone(), address_res)
}
}
ast::Expression::Builtin(_, _, ast::Builtin::Assert, args) => {
expr_assert(cfg, &args[0], contract_no, func, ns, vartab, opt)
}
ast::Expression::Builtin(_, _, ast::Builtin::Print, args) => {
let expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
cfg.add(vartab, Instr::Print { expr });
Expression::Poison
}
ast::Expression::Builtin(_, _, ast::Builtin::Require, args) => {
require(cfg, args, contract_no, func, ns, vartab, opt)
}
ast::Expression::Builtin(_, _, ast::Builtin::Revert, args) => {
revert(args, cfg, contract_no, func, ns, vartab, opt)
}
ast::Expression::Builtin(_, _, ast::Builtin::SelfDestruct, args) => {
self_destruct(args, cfg, contract_no, func, ns, vartab, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::PayableSend, args) => {
payable_send(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::PayableTransfer, args) => {
payable_transfer(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncode, args) => {
abi_encode(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodePacked, args) => {
abi_encode_packed(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodeWithSelector, args) => {
abi_encode_with_selector(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodeWithSignature, args) => {
abi_encode_with_signature(args, loc, cfg, contract_no, func, ns, vartab, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::AbiEncodeCall, args) => {
abi_encode_call(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::Builtin(loc, _, ast::Builtin::Gasprice, 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, builtin, args) => expr_builtin(
args,
cfg,
contract_no,
func,
ns,
vartab,
loc,
tys,
builtin,
opt,
),
ast::Expression::FormatString(loc, args) => {
format_string(args, cfg, contract_no, func, ns, vartab, loc, opt)
}
ast::Expression::AllocDynamicBytes(loc, ty, 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, *value),
ast::Expression::BytesLiteral(loc, ty, arr) => {
Expression::BytesLiteral(*loc, ty.clone(), arr.clone())
}
ast::Expression::CodeLiteral(loc, contract_no, _) => code(loc, *contract_no, ns, opt),
ast::Expression::NumberLiteral(loc, ty, n) => {
Expression::NumberLiteral(*loc, ty.clone(), n.clone())
}
ast::Expression::RationalNumberLiteral(loc, ty, n) => {
Expression::RationalNumberLiteral(*loc, ty.clone(), n.clone())
}
ast::Expression::Variable(loc, ty, var_no) => {
Expression::Variable(*loc, ty.clone(), *var_no)
}
ast::Expression::List(loc, elements) => Expression::List(
*loc,
elements
.iter()
.map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
.collect::<Vec<Expression>>(),
),
ast::Expression::GetRef(loc, ty, exp) => Expression::GetRef(
*loc,
ty.clone(),
Box::new(expression(exp, cfg, contract_no, func, ns, vartab, opt)),
),
}
}
fn memory_array_push(
ty: &Type,
vartab: &mut Vartable,
array: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
value: Expression,
loc: &pt::Loc,
opt: &Options,
) -> Expression {
let address_res = vartab.temp_anonymous(ty);
let array_pos = match expression(array, cfg, contract_no, func, ns, vartab, opt) {
Expression::Variable(_, _, pos) => {
vartab.set_dirty(pos);
pos
}
_ => 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, ty.clone(), address_res)
}
fn post_incdec(
vartab: &mut Vartable,
ty: &Type,
var: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
loc: &pt::Loc,
expr: &ast::Expression,
unchecked: &bool,
opt: &Options,
) -> Expression {
let res = vartab.temp_anonymous(ty);
let v = expression(var, cfg, contract_no, func, ns, vartab, opt);
let v = match var.ty() {
Type::Ref(ty) => Expression::Load(var.loc(), ty.as_ref().clone(), Box::new(v)),
Type::StorageRef(_, ty) => load_storage(&var.loc(), ty.as_ref(), v, cfg, vartab),
_ => v,
};
cfg.add(
vartab,
Instr::Set {
loc: v.loc(),
res,
expr: v,
},
);
let one = Box::new(Expression::NumberLiteral(*loc, ty.clone(), BigInt::one()));
let expr = match expr {
ast::Expression::PostDecrement { .. } => Expression::Subtract(
*loc,
ty.clone(),
*unchecked,
Box::new(Expression::Variable(*loc, ty.clone(), res)),
one,
),
ast::Expression::PostIncrement { .. } => Expression::Add(
*loc,
ty.clone(),
*unchecked,
Box::new(Expression::Variable(*loc, ty.clone(), res)),
one,
),
_ => unreachable!(),
};
match var {
ast::Expression::Variable(_, _, pos) => {
cfg.add(
vartab,
Instr::Set {
loc: expr.loc(),
res: *pos,
expr,
},
);
}
_ => {
let dest = expression(var, cfg, contract_no, func, ns, vartab, opt);
let res = vartab.temp_anonymous(ty);
cfg.add(
vartab,
Instr::Set {
loc: expr.loc(),
res,
expr,
},
);
match var.ty() {
Type::StorageRef(..) => {
cfg.add(
vartab,
Instr::SetStorage {
value: Expression::Variable(*loc, ty.clone(), res),
ty: ty.clone(),
storage: dest,
},
);
}
Type::Ref(_) => {
cfg.add(
vartab,
Instr::Store {
dest,
data: Expression::Variable(Loc::Codegen, ty.clone(), res),
},
);
}
_ => unreachable!(),
}
}
}
Expression::Variable(*loc, ty.clone(), res)
}
fn pre_incdec(
vartab: &mut Vartable,
ty: &Type,
var: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
loc: &pt::Loc,
expr: &ast::Expression,
unchecked: &bool,
opt: &Options,
) -> Expression {
let res = vartab.temp_anonymous(ty);
let v = expression(var, cfg, contract_no, func, ns, vartab, opt);
let v = match var.ty() {
Type::Ref(ty) => Expression::Load(var.loc(), ty.as_ref().clone(), Box::new(v)),
Type::StorageRef(_, ty) => load_storage(&var.loc(), ty.as_ref(), v, cfg, vartab),
_ => v,
};
let one = Box::new(Expression::NumberLiteral(*loc, ty.clone(), BigInt::one()));
let expr = match expr {
ast::Expression::PreDecrement { .. } => {
Expression::Subtract(*loc, ty.clone(), *unchecked, Box::new(v), one)
}
ast::Expression::PreIncrement { .. } => {
Expression::Add(*loc, ty.clone(), *unchecked, Box::new(v), one)
}
_ => unreachable!(),
};
cfg.add(
vartab,
Instr::Set {
loc: expr.loc(),
res,
expr,
},
);
match var {
ast::Expression::Variable(loc, _, pos) => {
cfg.add(
vartab,
Instr::Set {
loc: *loc,
res: *pos,
expr: Expression::Variable(*loc, ty.clone(), res),
},
);
}
_ => {
let dest = expression(var, cfg, contract_no, func, ns, vartab, opt);
match var.ty() {
Type::StorageRef(..) => {
cfg.add(
vartab,
Instr::SetStorage {
value: Expression::Variable(*loc, ty.clone(), res),
ty: ty.clone(),
storage: dest,
},
);
}
Type::Ref(_) => {
cfg.add(
vartab,
Instr::Store {
dest,
data: Expression::Variable(Loc::Codegen, ty.clone(), res),
},
);
}
_ => unreachable!(),
}
}
}
Expression::Variable(*loc, ty.clone(), res)
}
fn expr_or(
left: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
loc: &pt::Loc,
right: &ast::Expression,
opt: &Options,
) -> Expression {
let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
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, 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, Type::Bool, pos)
}
fn and(
left: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
loc: &pt::Loc,
right: &ast::Expression,
opt: &Options,
) -> Expression {
let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
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, 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, Type::Bool, pos)
}
fn expr_assert(
cfg: &mut ControlFlowGraph,
args: &ast::Expression,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
opt: &Options,
) -> Expression {
let true_ = cfg.new_basic_block("noassert".to_owned());
let false_ = cfg.new_basic_block("doassert".to_owned());
let cond = expression(args, cfg, contract_no, func, ns, vartab, opt);
cfg.add(
vartab,
Instr::BranchCond {
cond,
true_block: true_,
false_block: false_,
},
);
cfg.set_basic_block(false_);
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,
) -> 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 {
Target::Solana | Target::Substrate { .. } => {
if let Some(expr) = expr {
cfg.add(vartab, Instr::Print { expr });
}
assert_failure(&Loc::Codegen, None, ns, cfg, vartab);
}
_ => assert_failure(&Loc::Codegen, expr, ns, cfg, vartab),
}
cfg.set_basic_block(true_);
Expression::Poison
}
fn revert(
args: &[ast::Expression],
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
opt: &Options,
) -> Expression {
let expr = args
.get(0)
.map(|s| expression(s, cfg, contract_no, func, ns, vartab, opt));
assert_failure(&Loc::Codegen, expr, ns, cfg, vartab);
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 {
cfg.add(
vartab,
Instr::ExternalCall {
success: Some(success),
address: Some(address),
accounts: None,
seeds: None,
payload: Expression::AllocDynamicBytes(
*loc,
Type::DynamicBytes,
Box::new(Expression::NumberLiteral(
*loc,
Type::Uint(32),
BigInt::from(0),
)),
Some(vec![]),
),
value,
gas: Expression::NumberLiteral(*loc, Type::Uint(64), BigInt::from(i64::MAX)),
callty: CallTy::Regular,
},
);
}
Expression::Variable(*loc, Type::Bool, success)
}
fn payable_transfer(
args: &[ast::Expression],
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
loc: &pt::Loc,
opt: &Options,
) -> Expression {
let address = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
if ns.target != Target::EVM {
cfg.add(
vartab,
Instr::ValueTransfer {
success: None,
address,
value,
},
);
} else {
cfg.add(
vartab,
Instr::ExternalCall {
success: None,
accounts: None,
seeds: None,
address: Some(address),
payload: Expression::AllocDynamicBytes(
*loc,
Type::DynamicBytes,
Box::new(Expression::NumberLiteral(
*loc,
Type::Uint(32),
BigInt::from(0),
)),
Some(vec![]),
),
value,
gas: Expression::NumberLiteral(*loc, Type::Uint(64), BigInt::from(i64::MAX)),
callty: CallTy::Regular,
},
);
}
Expression::Poison
}
fn abi_encode(
args: &[ast::Expression],
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
loc: &pt::Loc,
opt: &Options,
) -> Expression {
let args = args
.iter()
.map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
.collect::<Vec<Expression>>();
let mut encoder = create_encoder(ns, false);
encoder.abi_encode(loc, args, ns, vartab, cfg).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 mut encoder = create_encoder(ns, true);
let (encoded, _) = encoder.abi_encode(loc, packed, ns, vartab, cfg);
encoded
}
fn encode_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);
let mut encoder = create_encoder(ns, false);
encoder.abi_encode(loc, encoder_args, ns, vartab, cfg).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_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,
vec![Type::Bytes(32)],
hash_algorithm,
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_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,
vec![Type::FunctionSelector],
ast::Builtin::FunctionSelector,
vec![args_iter.next().unwrap().clone()],
),
cfg,
contract_no,
func,
ns,
vartab,
opt,
);
let args = args_iter
.map(|v| expression(v, cfg, contract_no, func, ns, vartab, opt))
.collect::<Vec<Expression>>();
encode_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, vec![ty.clone()], Builtin::Gasprice, vec![]);
let units = expression(&expr[0], cfg, contract_no, func, ns, vartab, opt);
Expression::Multiply(*loc, ty, true, Box::new(units), Box::new(gasprice))
}
fn expr_builtin(
args: &[ast::Expression],
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
loc: &pt::Loc,
tys: &[Type],
builtin: &ast::Builtin,
opt: &Options,
) -> Expression {
match builtin {
ast::Builtin::WriteInt8
| ast::Builtin::WriteInt16LE
| ast::Builtin::WriteInt32LE
| ast::Builtin::WriteInt64LE
| ast::Builtin::WriteInt128LE
| ast::Builtin::WriteInt256LE
| ast::Builtin::WriteAddress
| ast::Builtin::WriteUint16LE
| ast::Builtin::WriteUint32LE
| ast::Builtin::WriteUint64LE
| ast::Builtin::WriteUint128LE
| ast::Builtin::WriteUint256LE => {
let buf = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
let offset = expression(&args[2], cfg, contract_no, func, ns, vartab, opt);
let cond = Expression::LessEqual(
*loc,
Box::new(Expression::Add(
*loc,
Type::Uint(32),
false,
Box::new(offset.clone()),
Box::new(Expression::NumberLiteral(
*loc,
Type::Uint(32),
BigInt::from(args[1].ty().bits(ns) / 8),
)),
)),
Box::new(Expression::Builtin(
*loc,
vec![Type::Uint(32)],
Builtin::ArrayLength,
vec![buf.clone()],
)),
);
let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
let in_bounds = cfg.new_basic_block("in_bounds".to_string());
cfg.add(
vartab,
Instr::BranchCond {
cond,
true_block: in_bounds,
false_block: out_of_bounds,
},
);
cfg.set_basic_block(out_of_bounds);
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(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,
vec![Type::Uint(32)],
Builtin::ArrayLength,
vec![data.clone()],
);
let cond = Expression::LessEqual(
*loc,
Box::new(Expression::Add(
*loc,
Type::Uint(32),
false,
Box::new(offset.clone()),
Box::new(size.clone()),
)),
Box::new(Expression::Builtin(
*loc,
vec![Type::Uint(32)],
Builtin::ArrayLength,
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);
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(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);
let cond = Expression::LessEqual(
*loc,
Box::new(Expression::Add(
*loc,
Type::Uint(32),
false,
Box::new(offset.clone()),
Box::new(Expression::NumberLiteral(
*loc,
Type::Uint(32),
BigInt::from(tys[0].bits(ns) / 8),
)),
)),
Box::new(Expression::Builtin(
*loc,
vec![Type::Uint(32)],
Builtin::ArrayLength,
vec![buf.clone()],
)),
);
let out_of_bounds = cfg.new_basic_block("out_of_bounds".to_string());
let in_bounds = cfg.new_basic_block("in_bounds".to_string());
cfg.add(
vartab,
Instr::BranchCond {
cond,
true_block: in_bounds,
false_block: out_of_bounds,
},
);
cfg.set_basic_block(out_of_bounds);
assert_failure(loc, None, ns, cfg, vartab);
cfg.set_basic_block(in_bounds);
Expression::Builtin(*loc, tys.to_vec(), builtin.into(), 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, tys[0].clone(), BigInt::zero());
let cond =
Expression::NotEqual(*loc, Box::new(zero.clone()), 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, tys.to_vec(), builtin.into(), 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, tys[0].clone(), 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 let Expression::Variable(_, _, num) = &arguments[0] {
if let Some(array_length_var) = cfg.array_lengths_temps.get(num) {
return Expression::Variable(*loc, Type::Uint(32), *array_length_var);
}
}
}
Expression::Builtin(*loc, tys.to_vec(), builtin.into(), 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, ty.clone(), Box::new(size), init.clone())
}
fn add(
loc: &pt::Loc,
ty: &Type,
unchecked: &bool,
left: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
right: &ast::Expression,
opt: &Options,
) -> Expression {
Expression::Add(
*loc,
ty.clone(),
*unchecked,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
)
}
fn substract(
loc: &pt::Loc,
ty: &Type,
unchecked: &bool,
left: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
right: &ast::Expression,
opt: &Options,
) -> Expression {
Expression::Subtract(
*loc,
ty.clone(),
*unchecked,
Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
)
}
fn checking_trunc(
loc: &pt::Loc,
expr: &ast::Expression,
ty: &Type,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
opt: &Options,
) -> Expression {
let bits = match ty {
Type::Uint(bits) => *bits as u32,
Type::Value => ns.value_length as u32 * 8,
_ => unreachable!(),
};
let source_ty = expr.ty();
let overflow = Expression::NumberLiteral(*loc, source_ty.clone(), BigInt::from(2u32).pow(bits));
let pos = vartab.temp(
&pt::Identifier {
name: "value".to_owned(),
loc: *loc,
},
&source_ty,
);
let expr = expression(expr, cfg, contract_no, func, ns, vartab, opt);
cfg.add(
vartab,
Instr::Set {
loc: 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,
Box::new(Expression::Variable(*loc, source_ty.clone(), pos)),
Box::new(overflow),
),
true_block: out_of_bounds,
false_block: in_bounds,
},
);
cfg.set_basic_block(out_of_bounds);
assert_failure(loc, None, ns, cfg, vartab);
cfg.set_basic_block(in_bounds);
Expression::Trunc(
*loc,
ty.clone(),
Box::new(Expression::Variable(*loc, source_ty, pos)),
)
}
fn format_string(
args: &[(FormatArg, ast::Expression)],
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
loc: &pt::Loc,
opt: &Options,
) -> Expression {
let args = args
.iter()
.map(|(spec, arg)| {
(
*spec,
expression(arg, cfg, contract_no, func, ns, vartab, opt),
)
})
.collect();
Expression::FormatString(*loc, args)
}
fn 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, ty.clone(), 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, Type::Bytes(selector_len), 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, pos) => {
cfg.add(
vartab,
Instr::Set {
loc: *loc,
res: *pos,
expr: cfg_right,
},
);
Expression::Variable(*loc, ty.clone(), *pos)
}
_ => {
let left_ty = left.ty();
let ty = left_ty.deref_memory();
let pos = vartab.temp_anonymous(ty);
let set_storage_bytes = if let ast::Expression::Subscript(_, _, array_ty, _, _) = &left
{
array_ty.is_storage_bytes()
} else {
false
};
let dest = expression(left, cfg, contract_no, func, ns, vartab, opt);
let cfg_right =
if !left_ty.is_contract_storage() && cfg_right.ty().is_fixed_reference_type() {
Expression::Load(pt::Loc::Codegen, cfg_right.ty(), Box::new(cfg_right))
} else {
cfg_right
};
cfg.add(
vartab,
Instr::Set {
loc: pt::Loc::Codegen,
res: pos,
expr: cfg_right,
},
);
match left_ty {
Type::StorageRef(..) if set_storage_bytes => {
if let Expression::Subscript(_, _, _, array, index) = dest {
cfg.add(
vartab,
Instr::SetStorageBytes {
value: Expression::Variable(left.loc(), ty.clone(), pos),
storage: *array,
offset: *index,
},
);
} else {
unreachable!();
}
}
Type::StorageRef(..) => {
cfg.add(
vartab,
Instr::SetStorage {
value: Expression::Variable(left.loc(), ty.clone(), pos),
ty: ty.deref_any().clone(),
storage: dest,
},
);
}
Type::Ref(_) => {
cfg.add(
vartab,
Instr::Store {
dest,
data: Expression::Variable(Loc::Codegen, ty.clone(), pos),
},
);
}
_ => unreachable!(),
}
Expression::Variable(left.loc(), ty.clone(), pos)
}
}
}
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(id.loc, ret.ty.clone(), 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(id.loc, ty.clone(), temp_pos));
}
cfg.add(
vartab,
Instr::Call {
res,
call: InternalCallTy::Dynamic(cfg_expr),
return_tys,
args,
},
);
return_values
} else {
cfg.add(
vartab,
Instr::Call {
res: Vec::new(),
return_tys: Vec::new(),
call: InternalCallTy::Dynamic(cfg_expr),
args,
},
);
vec![Expression::Poison]
}
} else {
unreachable!();
}
}
ast::Expression::ExternalFunctionCallRaw {
loc,
address,
args,
call_args,
ty,
} => {
let args = expression(args, cfg, 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(pt::Loc::Codegen, Type::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(),
},
);
vec![
Expression::Variable(*loc, Type::Bool, success),
Expression::ReturnData(*loc),
]
}
ast::Expression::ExternalFunctionCall {
loc,
function,
args,
returns,
call_args,
..
} => {
if let ast::Expression::ExternalFunction {
function_no,
address,
..
} = function.as_ref()
{
let dest_func = &ns.functions[*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(pt::Loc::Codegen, Type::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, Type::Bytes(selector.len() as u8), selector),
);
let mut encoder = create_encoder(ns, false);
let (payload, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
cfg.add(
vartab,
Instr::ExternalCall {
success: None,
accounts,
address: Some(address),
payload,
seeds,
value,
gas,
callty: CallTy::Regular,
},
);
if !dest_func.returns.is_empty() && returns[0] != Type::Void {
let encoder = create_encoder(ns, false);
let tys = dest_func
.returns
.iter()
.map(|e| e.ty.clone())
.collect::<Vec<Type>>();
encoder.abi_decode(
loc,
&Expression::ReturnData(*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(pt::Loc::Codegen, Type::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 mut encoder = create_encoder(ns, false);
let (payload, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
cfg.add(
vartab,
Instr::ExternalCall {
success: None,
accounts: None,
seeds: None,
address: Some(address),
payload,
value,
gas,
callty: CallTy::Regular,
},
);
if !func_returns.is_empty() && returns[0] != Type::Void {
let encoder = create_encoder(ns, false);
encoder.abi_decode(
loc,
&Expression::ReturnData(*loc),
returns,
ns,
vartab,
cfg,
None,
)
} else {
vec![Expression::Poison]
}
} else {
unreachable!();
}
}
ast::Expression::Builtin(loc, tys, ast::Builtin::AbiDecode, args) => {
let data = expression(&args[0], cfg, caller_contract_no, func, ns, vartab, opt);
let encoder = create_encoder(ns, false);
encoder.abi_decode(loc, &data, tys, ns, vartab, cfg, None)
}
_ => unreachable!(),
}
}
pub fn default_gas(ns: &Namespace) -> Expression {
Expression::NumberLiteral(
pt::Loc::Codegen,
Type::Uint(64),
if ns.target == Target::EVM {
BigInt::from(i64::MAX)
} else {
BigInt::zero()
},
)
}
fn array_subscript(
loc: &pt::Loc,
elem_ty: &Type,
array_ty: &Type,
array: &ast::Expression,
index: &ast::Expression,
cfg: &mut ControlFlowGraph,
contract_no: usize,
func: Option<&Function>,
ns: &Namespace,
vartab: &mut Vartable,
opt: &Options,
) -> Expression {
if array_ty.is_storage_bytes() {
return Expression::Subscript(
*loc,
elem_ty.clone(),
array_ty.clone(),
Box::new(expression(array, cfg, contract_no, func, ns, vartab, opt)),
Box::new(expression(index, cfg, contract_no, func, ns, vartab, opt)),
);
}
if array_ty.is_mapping() {
let array = expression(array, cfg, contract_no, func, ns, vartab, opt);
let index = expression(index, cfg, contract_no, func, ns, vartab, opt);
return if ns.target == Target::Solana {
Expression::Subscript(
*loc,
elem_ty.clone(),
array_ty.clone(),
Box::new(array),
Box::new(index),
)
} else {
Expression::Keccak256(*loc, array_ty.clone(), vec![array, index])
};
}
let mut array = expression(array, cfg, contract_no, func, ns, vartab, opt);
let index_ty = index.ty();
let index = expression(index, cfg, contract_no, func, ns, vartab, opt);
let index_loc = index.loc();
let index_width = index_ty.bits(ns);
let array_length = match array_ty.deref_any() {
Type::Bytes(n) => {
let ast_bigint = bigint_to_expression(
&array.loc(),
&BigInt::from(*n),
ns,
&mut Diagnostics::default(),
ResolveTo::Unknown,
)
.unwrap();
expression(&ast_bigint, cfg, contract_no, func, ns, vartab, opt)
}
Type::Array(..) => match array_ty.array_length() {
None => {
if let Type::StorageRef(..) = array_ty {
if ns.target == Target::Solana {
Expression::StorageArrayLength {
loc: *loc,
ty: ns.storage_type(),
array: Box::new(array.clone()),
elem_ty: array_ty.storage_array_elem().deref_into(),
}
} else {
let array_length =
load_storage(loc, &Type::Uint(256), array.clone(), cfg, vartab);
array = Expression::Keccak256(*loc, Type::Uint(256), vec![array]);
array_length
}
} else {
let mut returned = Expression::Builtin(
*loc,
vec![Type::Uint(32)],
Builtin::ArrayLength,
vec![array.clone()],
);
if let Expression::Variable(loc, _, num) = &array {
if let Some(array_length_var) = cfg.array_lengths_temps.get(num) {
returned =
Expression::Variable(*loc, Type::Uint(32), *array_length_var);
}
}
returned
}
}
Some(l) => {
let ast_big_int = bigint_to_expression(
loc,
l,
ns,
&mut Diagnostics::default(),
ResolveTo::Unknown,
)
.unwrap();
expression(&ast_big_int, cfg, contract_no, func, ns, vartab, opt)
}
},
Type::DynamicBytes => Expression::Builtin(
*loc,
vec![Type::Uint(32)],
Builtin::ArrayLength,
vec![array.clone()],
),
_ => {
unreachable!();
}
};
let array_width = array_length.ty().bits(ns);
let width = std::cmp::max(array_width, index.ty().bits(ns));
let coerced_ty = Type::Uint(width);
let pos = vartab.temp(
&pt::Identifier {
name: "index".to_owned(),
loc: *loc,
},
&coerced_ty,
);
let expr = index.cast(&coerced_ty, ns);
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,
Box::new(Expression::Variable(index_loc, coerced_ty.clone(), pos)),
Box::new(array_length.cast(&coerced_ty, ns)),
),
true_block: out_of_bounds,
false_block: in_bounds,
},
);
cfg.set_basic_block(out_of_bounds);
assert_failure(loc, None, ns, cfg, vartab);
cfg.set_basic_block(in_bounds);
if let Type::StorageRef(_, ty) = &array_ty {
let elem_ty = ty.storage_array_elem();
let slot_ty = ns.storage_type();
if ns.target == Target::Solana {
if ty.array_length().is_some() && ty.is_sparse_solana(ns) {
let index =
Expression::Variable(index_loc, coerced_ty, pos).cast(&Type::Uint(256), ns);
Expression::Subscript(
*loc,
elem_ty,
array_ty.clone(),
Box::new(array),
Box::new(index),
)
} else {
let index = Expression::Variable(index_loc, coerced_ty, pos).cast(&slot_ty, ns);
if ty.array_length().is_some() {
let elem_size = elem_ty.deref_any().solana_storage_size(ns);
Expression::Add(
*loc,
elem_ty,
true,
Box::new(array),
Box::new(Expression::Multiply(
*loc,
slot_ty.clone(),
true,
Box::new(index),
Box::new(Expression::NumberLiteral(*loc, slot_ty, elem_size)),
)),
)
} else {
Expression::Subscript(
*loc,
elem_ty,
array_ty.clone(),
Box::new(array),
Box::new(index),
)
}
}
} else {
let elem_size = elem_ty.storage_slots(ns);
if let Expression::NumberLiteral(_, _, arr_length) = &array_length {
if arr_length.mul(elem_size.clone()).to_u64().is_some() {
return Expression::Add(
*loc,
elem_ty,
true,
Box::new(array),
Box::new(Expression::ZeroExt(
*loc,
slot_ty,
Box::new(Expression::Multiply(
*loc,
Type::Uint(64),
true,
Box::new(
Expression::Variable(index_loc, coerced_ty, pos)
.cast(&Type::Uint(64), ns),
),
Box::new(Expression::NumberLiteral(
*loc,
Type::Uint(64),
elem_size,
)),
)),
)),
);
}
}
array_offset(
loc,
array,
Expression::Variable(index_loc, coerced_ty, pos).cast(&ns.storage_type(), ns),
elem_ty,
ns,
)
}
} else {
match array_ty.deref_memory() {
Type::Bytes(array_length) => {
let res_ty = Type::Bytes(1);
let from_ty = Type::Bytes(*array_length);
let index_ty = Type::Uint(*array_length as u16 * 8);
let to_width = array_ty.bits(ns);
let shift_arg_raw = Expression::Variable(index_loc, coerced_ty.clone(), pos);
let shift_arg = if index_width == to_width {
shift_arg_raw
} else if index_width < to_width && array_ty.is_signed_int() {
Expression::SignExt(*loc, array_ty.clone(), Box::new(shift_arg_raw))
} else if index_width < to_width && !array_ty.is_signed_int() {
Expression::ZeroExt(*loc, array_ty.clone(), Box::new(shift_arg_raw))
} else {
Expression::Trunc(*loc, array_ty.clone(), Box::new(shift_arg_raw))
};
Expression::Trunc(
*loc,
res_ty,
Box::new(Expression::ShiftRight(
*loc,
from_ty,
Box::new(array),
Box::new(Expression::ShiftLeft(
*loc,
index_ty.clone(),
Box::new(Expression::Subtract(
*loc,
index_ty.clone(),
true,
Box::new(Expression::NumberLiteral(
*loc,
index_ty.clone(),
BigInt::from_u8(array_length - 1).unwrap(),
)),
Box::new(shift_arg),
)),
Box::new(Expression::NumberLiteral(
*loc,
index_ty,
BigInt::from_u8(3).unwrap(),
)),
)),
false,
)),
)
}
Type::DynamicBytes | Type::Array(..) => Expression::Subscript(
*loc,
elem_ty.clone(),
array_ty.clone(),
Box::new(array),
Box::new(Expression::Variable(index_loc, coerced_ty, pos)),
),
_ => {
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()),
}
}
pub fn load_storage(
loc: &pt::Loc,
ty: &Type,
storage: Expression,
cfg: &mut ControlFlowGraph,
vartab: &mut Vartable,
) -> Expression {
let res = vartab.temp_anonymous(ty);
cfg.add(
vartab,
Instr::LoadStorage {
res,
ty: ty.clone(),
storage,
},
);
Expression::Variable(*loc, ty.clone(), res)
}
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, Type::Uint(32), dims);
cfg.add(
vartab,
Instr::Set {
loc: *loc,
res: memory_array,
expr: Expression::AllocDynamicBytes(
*loc,
ty.clone(),
Box::new(array_size.clone()),
None,
),
},
);
let elements = if let Expression::ArrayLiteral(_, _, _, items) = expr {
items
} else {
unreachable!()
};
for (item_no, item) in elements.iter().enumerate() {
cfg.add(
vartab,
Instr::Store {
dest: Expression::Subscript(
*loc,
Type::Ref(Box::new(elem_ty.clone())),
ty.clone(),
Box::new(Expression::Variable(*loc, ty.clone(), memory_array)),
Box::new(Expression::NumberLiteral(
*loc,
Type::Uint(32),
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, ty.clone(), memory_array)
}
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::Codegen, Type::Uint(32), BigInt::from(selector));
let args = vec![selector, arg.unwrap()];
let mut encoder = create_encoder(ns, false);
let (encoded_buffer, _) = encoder.abi_encode(loc, args, ns, vartab, cfg);
cfg.add(
vartab,
Instr::AssertFailure {
encoded_args: Some(encoded_buffer),
},
)
}
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, Type::Uint(32), code.len().into());
Expression::AllocDynamicBytes(*loc, Type::DynamicBytes, size.into(), Some(code))
}