| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- // SPDX-License-Identifier: Apache-2.0
- use crate::codegen::Expression;
- use crate::sema::ast;
- use num_bigint::BigInt;
- use num_traits::FromPrimitive;
- use num_traits::One;
- use num_traits::Zero;
- use super::expression::{expression, load_storage, log_runtime_error};
- use super::Options;
- use super::{
- cfg::{ControlFlowGraph, Instr},
- vartable::Vartable,
- };
- use crate::codegen::expression::assert_failure;
- use crate::sema::ast::{Function, Namespace, RetrieveType, Type};
- use solang_parser::pt;
- /// Given a storage slot which is the start of the array, calculate the
- /// offset of the array element. This function exists to avoid doing
- /// 256 bit multiply if possible.
- pub fn array_offset(
- loc: &pt::Loc,
- start: Expression,
- index: Expression,
- elem_ty: Type,
- ns: &Namespace,
- ) -> Expression {
- let elem_size = elem_ty.storage_slots(ns);
- let slot_ty = ns.storage_type();
- // the index needs to be cast to i256 and multiplied by the number
- // of slots for each element
- if elem_size == BigInt::one() {
- Expression::Add(*loc, slot_ty, true, Box::new(start), Box::new(index))
- } else if (elem_size.clone() & (elem_size.clone() - BigInt::one())) == BigInt::zero() {
- // elem_size is power of 2
- Expression::Add(
- *loc,
- slot_ty.clone(),
- true,
- Box::new(start),
- Box::new(Expression::ShiftLeft(
- *loc,
- slot_ty.clone(),
- Box::new(index),
- Box::new(Expression::NumberLiteral(
- *loc,
- slot_ty,
- BigInt::from_u64(elem_size.bits() - 1).unwrap(),
- )),
- )),
- )
- } else {
- Expression::Add(
- *loc,
- slot_ty.clone(),
- true,
- Box::new(start),
- Box::new(Expression::Multiply(
- *loc,
- slot_ty.clone(),
- true,
- Box::new(index),
- Box::new(Expression::NumberLiteral(*loc, slot_ty, elem_size)),
- )),
- )
- }
- }
- /// Push() method on dynamic array in storage
- pub fn storage_slots_array_push(
- loc: &pt::Loc,
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- // set array+length to val_expr
- let slot_ty = ns.storage_type();
- let length_pos = vartab.temp_anonymous(&slot_ty);
- let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let expr = load_storage(loc, &slot_ty, var_expr.clone(), cfg, vartab);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: length_pos,
- expr,
- },
- );
- let elem_ty = args[0].ty().storage_array_elem();
- let entry_pos = vartab.temp_anonymous(&slot_ty);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: entry_pos,
- expr: array_offset(
- loc,
- Expression::Keccak256(*loc, slot_ty.clone(), vec![var_expr.clone()]),
- Expression::Variable(*loc, slot_ty.clone(), length_pos),
- elem_ty.clone(),
- ns,
- ),
- },
- );
- if args.len() == 2 {
- let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::SetStorage {
- ty: elem_ty.clone(),
- value,
- storage: Expression::Variable(*loc, slot_ty.clone(), entry_pos),
- },
- );
- }
- // increase length
- let new_length = Expression::Add(
- *loc,
- slot_ty.clone(),
- true,
- Box::new(Expression::Variable(*loc, slot_ty.clone(), length_pos)),
- Box::new(Expression::NumberLiteral(
- *loc,
- slot_ty.clone(),
- BigInt::one(),
- )),
- );
- cfg.add(
- vartab,
- Instr::SetStorage {
- ty: slot_ty,
- value: new_length,
- storage: var_expr,
- },
- );
- if args.len() == 1 {
- Expression::Variable(*loc, elem_ty, entry_pos)
- } else {
- Expression::Poison
- }
- }
- /// Pop() method on dynamic array in storage
- pub fn storage_slots_array_pop(
- loc: &pt::Loc,
- args: &[ast::Expression],
- return_ty: &Type,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- // set array+length to val_expr
- let slot_ty = ns.storage_type();
- let length_ty = ns.storage_type();
- let length_pos = vartab.temp_anonymous(&slot_ty);
- let ty = args[0].ty();
- let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let expr = load_storage(loc, &length_ty, var_expr.clone(), cfg, vartab);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: length_pos,
- expr,
- },
- );
- let empty_array = cfg.new_basic_block("empty_array".to_string());
- let has_elements = cfg.new_basic_block("has_elements".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: Expression::Equal(
- *loc,
- Box::new(Expression::Variable(*loc, length_ty.clone(), length_pos)),
- Box::new(Expression::NumberLiteral(
- *loc,
- length_ty.clone(),
- BigInt::zero(),
- )),
- ),
- true_block: empty_array,
- false_block: has_elements,
- },
- );
- cfg.set_basic_block(empty_array);
- log_runtime_error(
- opt.log_runtime_errors,
- "pop from empty storage array",
- *loc,
- cfg,
- vartab,
- ns,
- );
- assert_failure(loc, None, ns, cfg, vartab);
- cfg.set_basic_block(has_elements);
- let new_length = vartab.temp_anonymous(&slot_ty);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: new_length,
- expr: Expression::Subtract(
- *loc,
- length_ty.clone(),
- true,
- Box::new(Expression::Variable(*loc, length_ty.clone(), length_pos)),
- Box::new(Expression::NumberLiteral(*loc, length_ty, BigInt::one())),
- ),
- },
- );
- // The array element will be loaded before clearing. So, the return
- // type of pop() is the derefenced array dereference
- let elem_ty = ty.storage_array_elem().deref_any().clone();
- let entry_pos = vartab.temp_anonymous(&slot_ty);
- cfg.add(
- vartab,
- Instr::Set {
- loc: pt::Loc::Codegen,
- res: entry_pos,
- expr: array_offset(
- loc,
- Expression::Keccak256(*loc, slot_ty.clone(), vec![var_expr.clone()]),
- Expression::Variable(*loc, slot_ty.clone(), new_length),
- elem_ty.clone(),
- ns,
- ),
- },
- );
- let val = if *return_ty != Type::Void {
- let res_pos = vartab.temp_anonymous(&elem_ty);
- let expr = load_storage(
- loc,
- &elem_ty,
- Expression::Variable(*loc, elem_ty.clone(), entry_pos),
- cfg,
- vartab,
- );
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: res_pos,
- expr,
- },
- );
- Expression::Variable(*loc, elem_ty.clone(), res_pos)
- } else {
- Expression::Undefined(elem_ty.clone())
- };
- cfg.add(
- vartab,
- Instr::ClearStorage {
- ty: elem_ty,
- storage: Expression::Variable(*loc, slot_ty.clone(), entry_pos),
- },
- );
- // set decrease length
- cfg.add(
- vartab,
- Instr::SetStorage {
- ty: slot_ty.clone(),
- value: Expression::Variable(*loc, slot_ty, new_length),
- storage: var_expr,
- },
- );
- val
- }
- /// Push() method on array or bytes in storage
- pub fn array_push(
- loc: &pt::Loc,
- args: &[ast::Expression],
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let storage = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let mut ty = args[0].ty().storage_array_elem();
- let value = if args.len() > 1 {
- Some(expression(
- &args[1],
- cfg,
- contract_no,
- func,
- ns,
- vartab,
- opt,
- ))
- } else {
- ty.deref_any().default(ns)
- };
- if !ty.is_reference_type(ns) {
- ty = ty.deref_into();
- }
- let res = vartab.temp_anonymous(&ty);
- cfg.add(
- vartab,
- Instr::PushStorage {
- res,
- ty: ty.deref_any().clone(),
- storage,
- value,
- },
- );
- Expression::Variable(*loc, ty, res)
- }
- /// Pop() method on array or bytes in storage
- pub fn array_pop(
- loc: &pt::Loc,
- args: &[ast::Expression],
- return_ty: &Type,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: Option<&Function>,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) -> Expression {
- let storage = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
- let ty = args[0].ty().storage_array_elem().deref_into();
- let res = if *return_ty != Type::Void {
- Some(vartab.temp_anonymous(&ty))
- } else {
- None
- };
- cfg.add(
- vartab,
- Instr::PopStorage {
- res,
- ty: ty.clone(),
- storage,
- },
- );
- if let Some(res) = res {
- Expression::Variable(*loc, ty, res)
- } else {
- Expression::Undefined(ty)
- }
- }
|