storage.rs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::codegen::Expression;
  3. use crate::sema::ast;
  4. use num_bigint::BigInt;
  5. use num_traits::FromPrimitive;
  6. use num_traits::One;
  7. use num_traits::Zero;
  8. use super::expression::{expression, load_storage, log_runtime_error};
  9. use super::Options;
  10. use super::{
  11. cfg::{ControlFlowGraph, Instr},
  12. vartable::Vartable,
  13. };
  14. use crate::codegen::expression::assert_failure;
  15. use crate::sema::ast::{Function, Namespace, RetrieveType, Type};
  16. use solang_parser::pt;
  17. /// Given a storage slot which is the start of the array, calculate the
  18. /// offset of the array element. This function exists to avoid doing
  19. /// 256 bit multiply if possible.
  20. pub fn array_offset(
  21. loc: &pt::Loc,
  22. start: Expression,
  23. index: Expression,
  24. elem_ty: Type,
  25. ns: &Namespace,
  26. ) -> Expression {
  27. let elem_size = elem_ty.storage_slots(ns);
  28. let slot_ty = ns.storage_type();
  29. // the index needs to be cast to i256 and multiplied by the number
  30. // of slots for each element
  31. if elem_size == BigInt::one() {
  32. Expression::Add(*loc, slot_ty, true, Box::new(start), Box::new(index))
  33. } else if (elem_size.clone() & (elem_size.clone() - BigInt::one())) == BigInt::zero() {
  34. // elem_size is power of 2
  35. Expression::Add(
  36. *loc,
  37. slot_ty.clone(),
  38. true,
  39. Box::new(start),
  40. Box::new(Expression::ShiftLeft(
  41. *loc,
  42. slot_ty.clone(),
  43. Box::new(index),
  44. Box::new(Expression::NumberLiteral(
  45. *loc,
  46. slot_ty,
  47. BigInt::from_u64(elem_size.bits() - 1).unwrap(),
  48. )),
  49. )),
  50. )
  51. } else {
  52. Expression::Add(
  53. *loc,
  54. slot_ty.clone(),
  55. true,
  56. Box::new(start),
  57. Box::new(Expression::Multiply(
  58. *loc,
  59. slot_ty.clone(),
  60. true,
  61. Box::new(index),
  62. Box::new(Expression::NumberLiteral(*loc, slot_ty, elem_size)),
  63. )),
  64. )
  65. }
  66. }
  67. /// Push() method on dynamic array in storage
  68. pub fn storage_slots_array_push(
  69. loc: &pt::Loc,
  70. args: &[ast::Expression],
  71. cfg: &mut ControlFlowGraph,
  72. contract_no: usize,
  73. func: Option<&Function>,
  74. ns: &Namespace,
  75. vartab: &mut Vartable,
  76. opt: &Options,
  77. ) -> Expression {
  78. // set array+length to val_expr
  79. let slot_ty = ns.storage_type();
  80. let length_pos = vartab.temp_anonymous(&slot_ty);
  81. let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  82. let expr = load_storage(loc, &slot_ty, var_expr.clone(), cfg, vartab);
  83. cfg.add(
  84. vartab,
  85. Instr::Set {
  86. loc: pt::Loc::Codegen,
  87. res: length_pos,
  88. expr,
  89. },
  90. );
  91. let elem_ty = args[0].ty().storage_array_elem();
  92. let entry_pos = vartab.temp_anonymous(&slot_ty);
  93. cfg.add(
  94. vartab,
  95. Instr::Set {
  96. loc: pt::Loc::Codegen,
  97. res: entry_pos,
  98. expr: array_offset(
  99. loc,
  100. Expression::Keccak256(*loc, slot_ty.clone(), vec![var_expr.clone()]),
  101. Expression::Variable(*loc, slot_ty.clone(), length_pos),
  102. elem_ty.clone(),
  103. ns,
  104. ),
  105. },
  106. );
  107. if args.len() == 2 {
  108. let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
  109. cfg.add(
  110. vartab,
  111. Instr::SetStorage {
  112. ty: elem_ty.clone(),
  113. value,
  114. storage: Expression::Variable(*loc, slot_ty.clone(), entry_pos),
  115. },
  116. );
  117. }
  118. // increase length
  119. let new_length = Expression::Add(
  120. *loc,
  121. slot_ty.clone(),
  122. true,
  123. Box::new(Expression::Variable(*loc, slot_ty.clone(), length_pos)),
  124. Box::new(Expression::NumberLiteral(
  125. *loc,
  126. slot_ty.clone(),
  127. BigInt::one(),
  128. )),
  129. );
  130. cfg.add(
  131. vartab,
  132. Instr::SetStorage {
  133. ty: slot_ty,
  134. value: new_length,
  135. storage: var_expr,
  136. },
  137. );
  138. if args.len() == 1 {
  139. Expression::Variable(*loc, elem_ty, entry_pos)
  140. } else {
  141. Expression::Poison
  142. }
  143. }
  144. /// Pop() method on dynamic array in storage
  145. pub fn storage_slots_array_pop(
  146. loc: &pt::Loc,
  147. args: &[ast::Expression],
  148. return_ty: &Type,
  149. cfg: &mut ControlFlowGraph,
  150. contract_no: usize,
  151. func: Option<&Function>,
  152. ns: &Namespace,
  153. vartab: &mut Vartable,
  154. opt: &Options,
  155. ) -> Expression {
  156. // set array+length to val_expr
  157. let slot_ty = ns.storage_type();
  158. let length_ty = ns.storage_type();
  159. let length_pos = vartab.temp_anonymous(&slot_ty);
  160. let ty = args[0].ty();
  161. let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  162. let expr = load_storage(loc, &length_ty, var_expr.clone(), cfg, vartab);
  163. cfg.add(
  164. vartab,
  165. Instr::Set {
  166. loc: pt::Loc::Codegen,
  167. res: length_pos,
  168. expr,
  169. },
  170. );
  171. let empty_array = cfg.new_basic_block("empty_array".to_string());
  172. let has_elements = cfg.new_basic_block("has_elements".to_string());
  173. cfg.add(
  174. vartab,
  175. Instr::BranchCond {
  176. cond: Expression::Equal(
  177. *loc,
  178. Box::new(Expression::Variable(*loc, length_ty.clone(), length_pos)),
  179. Box::new(Expression::NumberLiteral(
  180. *loc,
  181. length_ty.clone(),
  182. BigInt::zero(),
  183. )),
  184. ),
  185. true_block: empty_array,
  186. false_block: has_elements,
  187. },
  188. );
  189. cfg.set_basic_block(empty_array);
  190. log_runtime_error(
  191. opt.log_runtime_errors,
  192. "pop from empty storage array",
  193. *loc,
  194. cfg,
  195. vartab,
  196. ns,
  197. );
  198. assert_failure(loc, None, ns, cfg, vartab);
  199. cfg.set_basic_block(has_elements);
  200. let new_length = vartab.temp_anonymous(&slot_ty);
  201. cfg.add(
  202. vartab,
  203. Instr::Set {
  204. loc: pt::Loc::Codegen,
  205. res: new_length,
  206. expr: Expression::Subtract(
  207. *loc,
  208. length_ty.clone(),
  209. true,
  210. Box::new(Expression::Variable(*loc, length_ty.clone(), length_pos)),
  211. Box::new(Expression::NumberLiteral(*loc, length_ty, BigInt::one())),
  212. ),
  213. },
  214. );
  215. // The array element will be loaded before clearing. So, the return
  216. // type of pop() is the derefenced array dereference
  217. let elem_ty = ty.storage_array_elem().deref_any().clone();
  218. let entry_pos = vartab.temp_anonymous(&slot_ty);
  219. cfg.add(
  220. vartab,
  221. Instr::Set {
  222. loc: pt::Loc::Codegen,
  223. res: entry_pos,
  224. expr: array_offset(
  225. loc,
  226. Expression::Keccak256(*loc, slot_ty.clone(), vec![var_expr.clone()]),
  227. Expression::Variable(*loc, slot_ty.clone(), new_length),
  228. elem_ty.clone(),
  229. ns,
  230. ),
  231. },
  232. );
  233. let val = if *return_ty != Type::Void {
  234. let res_pos = vartab.temp_anonymous(&elem_ty);
  235. let expr = load_storage(
  236. loc,
  237. &elem_ty,
  238. Expression::Variable(*loc, elem_ty.clone(), entry_pos),
  239. cfg,
  240. vartab,
  241. );
  242. cfg.add(
  243. vartab,
  244. Instr::Set {
  245. loc: *loc,
  246. res: res_pos,
  247. expr,
  248. },
  249. );
  250. Expression::Variable(*loc, elem_ty.clone(), res_pos)
  251. } else {
  252. Expression::Undefined(elem_ty.clone())
  253. };
  254. cfg.add(
  255. vartab,
  256. Instr::ClearStorage {
  257. ty: elem_ty,
  258. storage: Expression::Variable(*loc, slot_ty.clone(), entry_pos),
  259. },
  260. );
  261. // set decrease length
  262. cfg.add(
  263. vartab,
  264. Instr::SetStorage {
  265. ty: slot_ty.clone(),
  266. value: Expression::Variable(*loc, slot_ty, new_length),
  267. storage: var_expr,
  268. },
  269. );
  270. val
  271. }
  272. /// Push() method on array or bytes in storage
  273. pub fn array_push(
  274. loc: &pt::Loc,
  275. args: &[ast::Expression],
  276. cfg: &mut ControlFlowGraph,
  277. contract_no: usize,
  278. func: Option<&Function>,
  279. ns: &Namespace,
  280. vartab: &mut Vartable,
  281. opt: &Options,
  282. ) -> Expression {
  283. let storage = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  284. let mut ty = args[0].ty().storage_array_elem();
  285. let value = if args.len() > 1 {
  286. Some(expression(
  287. &args[1],
  288. cfg,
  289. contract_no,
  290. func,
  291. ns,
  292. vartab,
  293. opt,
  294. ))
  295. } else {
  296. ty.deref_any().default(ns)
  297. };
  298. if !ty.is_reference_type(ns) {
  299. ty = ty.deref_into();
  300. }
  301. let res = vartab.temp_anonymous(&ty);
  302. cfg.add(
  303. vartab,
  304. Instr::PushStorage {
  305. res,
  306. ty: ty.deref_any().clone(),
  307. storage,
  308. value,
  309. },
  310. );
  311. Expression::Variable(*loc, ty, res)
  312. }
  313. /// Pop() method on array or bytes in storage
  314. pub fn array_pop(
  315. loc: &pt::Loc,
  316. args: &[ast::Expression],
  317. return_ty: &Type,
  318. cfg: &mut ControlFlowGraph,
  319. contract_no: usize,
  320. func: Option<&Function>,
  321. ns: &Namespace,
  322. vartab: &mut Vartable,
  323. opt: &Options,
  324. ) -> Expression {
  325. let storage = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  326. let ty = args[0].ty().storage_array_elem().deref_into();
  327. let res = if *return_ty != Type::Void {
  328. Some(vartab.temp_anonymous(&ty))
  329. } else {
  330. None
  331. };
  332. cfg.add(
  333. vartab,
  334. Instr::PopStorage {
  335. res,
  336. ty: ty.clone(),
  337. storage,
  338. },
  339. );
  340. if let Some(res) = res {
  341. Expression::Variable(*loc, ty, res)
  342. } else {
  343. Expression::Undefined(ty)
  344. }
  345. }