storage.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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 {
  33. loc: *loc,
  34. ty: slot_ty,
  35. overflowing: true,
  36. left: Box::new(start),
  37. right: Box::new(index),
  38. }
  39. } else if (elem_size.clone() & (elem_size.clone() - BigInt::one())) == BigInt::zero() {
  40. // elem_size is power of 2
  41. Expression::Add {
  42. loc: *loc,
  43. ty: slot_ty.clone(),
  44. overflowing: true,
  45. left: Box::new(start),
  46. right: Box::new(Expression::ShiftLeft {
  47. loc: *loc,
  48. ty: slot_ty.clone(),
  49. left: Box::new(index),
  50. right: Box::new(Expression::NumberLiteral {
  51. loc: *loc,
  52. ty: slot_ty,
  53. value: BigInt::from_u64(elem_size.bits() - 1).unwrap(),
  54. }),
  55. }),
  56. }
  57. } else {
  58. Expression::Add {
  59. loc: *loc,
  60. ty: slot_ty.clone(),
  61. overflowing: true,
  62. left: Box::new(start),
  63. right: Box::new(Expression::Multiply {
  64. loc: *loc,
  65. ty: slot_ty.clone(),
  66. overflowing: true,
  67. left: Box::new(index),
  68. right: Box::new(Expression::NumberLiteral {
  69. loc: *loc,
  70. ty: slot_ty,
  71. value: elem_size,
  72. }),
  73. }),
  74. }
  75. }
  76. }
  77. /// Push() method on dynamic array in storage
  78. pub fn storage_slots_array_push(
  79. loc: &pt::Loc,
  80. args: &[ast::Expression],
  81. cfg: &mut ControlFlowGraph,
  82. contract_no: usize,
  83. func: Option<&Function>,
  84. ns: &Namespace,
  85. vartab: &mut Vartable,
  86. opt: &Options,
  87. ) -> Expression {
  88. // set array+length to val_expr
  89. let slot_ty = ns.storage_type();
  90. let length_pos = vartab.temp_anonymous(&slot_ty);
  91. let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  92. let expr = load_storage(loc, &slot_ty, var_expr.clone(), cfg, vartab);
  93. cfg.add(
  94. vartab,
  95. Instr::Set {
  96. loc: pt::Loc::Codegen,
  97. res: length_pos,
  98. expr,
  99. },
  100. );
  101. let elem_ty = args[0].ty().storage_array_elem();
  102. let entry_pos = vartab.temp_anonymous(&slot_ty);
  103. cfg.add(
  104. vartab,
  105. Instr::Set {
  106. loc: pt::Loc::Codegen,
  107. res: entry_pos,
  108. expr: array_offset(
  109. loc,
  110. Expression::Keccak256 {
  111. loc: *loc,
  112. ty: slot_ty.clone(),
  113. exprs: vec![var_expr.clone()],
  114. },
  115. Expression::Variable {
  116. loc: *loc,
  117. ty: slot_ty.clone(),
  118. var_no: length_pos,
  119. },
  120. elem_ty.clone(),
  121. ns,
  122. ),
  123. },
  124. );
  125. if args.len() == 2 {
  126. let value = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
  127. cfg.add(
  128. vartab,
  129. Instr::SetStorage {
  130. ty: elem_ty.clone(),
  131. value,
  132. storage: Expression::Variable {
  133. loc: *loc,
  134. ty: slot_ty.clone(),
  135. var_no: entry_pos,
  136. },
  137. },
  138. );
  139. }
  140. // increase length
  141. let new_length = Expression::Add {
  142. loc: *loc,
  143. ty: slot_ty.clone(),
  144. overflowing: true,
  145. left: Box::new(Expression::Variable {
  146. loc: *loc,
  147. ty: slot_ty.clone(),
  148. var_no: length_pos,
  149. }),
  150. right: Box::new(Expression::NumberLiteral {
  151. loc: *loc,
  152. ty: slot_ty.clone(),
  153. value: BigInt::one(),
  154. }),
  155. };
  156. cfg.add(
  157. vartab,
  158. Instr::SetStorage {
  159. ty: slot_ty,
  160. value: new_length,
  161. storage: var_expr,
  162. },
  163. );
  164. if args.len() == 1 {
  165. Expression::Variable {
  166. loc: *loc,
  167. ty: elem_ty,
  168. var_no: entry_pos,
  169. }
  170. } else {
  171. Expression::Poison
  172. }
  173. }
  174. /// Pop() method on dynamic array in storage
  175. pub fn storage_slots_array_pop(
  176. loc: &pt::Loc,
  177. args: &[ast::Expression],
  178. return_ty: &Type,
  179. cfg: &mut ControlFlowGraph,
  180. contract_no: usize,
  181. func: Option<&Function>,
  182. ns: &Namespace,
  183. vartab: &mut Vartable,
  184. opt: &Options,
  185. ) -> Expression {
  186. // set array+length to val_expr
  187. let slot_ty = ns.storage_type();
  188. let length_ty = ns.storage_type();
  189. let length_pos = vartab.temp_anonymous(&slot_ty);
  190. let ty = args[0].ty();
  191. let var_expr = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  192. let expr = load_storage(loc, &length_ty, var_expr.clone(), cfg, vartab);
  193. cfg.add(
  194. vartab,
  195. Instr::Set {
  196. loc: pt::Loc::Codegen,
  197. res: length_pos,
  198. expr,
  199. },
  200. );
  201. let empty_array = cfg.new_basic_block("empty_array".to_string());
  202. let has_elements = cfg.new_basic_block("has_elements".to_string());
  203. cfg.add(
  204. vartab,
  205. Instr::BranchCond {
  206. cond: Expression::Equal {
  207. loc: *loc,
  208. left: Box::new(Expression::Variable {
  209. loc: *loc,
  210. ty: length_ty.clone(),
  211. var_no: length_pos,
  212. }),
  213. right: Box::new(Expression::NumberLiteral {
  214. loc: *loc,
  215. ty: length_ty.clone(),
  216. value: BigInt::zero(),
  217. }),
  218. },
  219. true_block: empty_array,
  220. false_block: has_elements,
  221. },
  222. );
  223. cfg.set_basic_block(empty_array);
  224. log_runtime_error(
  225. opt.log_runtime_errors,
  226. "pop from empty storage array",
  227. *loc,
  228. cfg,
  229. vartab,
  230. ns,
  231. );
  232. assert_failure(loc, None, ns, cfg, vartab);
  233. cfg.set_basic_block(has_elements);
  234. let new_length = vartab.temp_anonymous(&slot_ty);
  235. cfg.add(
  236. vartab,
  237. Instr::Set {
  238. loc: pt::Loc::Codegen,
  239. res: new_length,
  240. expr: Expression::Subtract {
  241. loc: *loc,
  242. ty: length_ty.clone(),
  243. overflowing: true,
  244. left: Box::new(Expression::Variable {
  245. loc: *loc,
  246. ty: length_ty.clone(),
  247. var_no: length_pos,
  248. }),
  249. right: Box::new(Expression::NumberLiteral {
  250. loc: *loc,
  251. ty: length_ty,
  252. value: BigInt::one(),
  253. }),
  254. },
  255. },
  256. );
  257. // The array element will be loaded before clearing. So, the return
  258. // type of pop() is the derefenced array dereference
  259. let elem_ty = ty.storage_array_elem().deref_any().clone();
  260. let entry_pos = vartab.temp_anonymous(&slot_ty);
  261. cfg.add(
  262. vartab,
  263. Instr::Set {
  264. loc: pt::Loc::Codegen,
  265. res: entry_pos,
  266. expr: array_offset(
  267. loc,
  268. Expression::Keccak256 {
  269. loc: *loc,
  270. ty: slot_ty.clone(),
  271. exprs: vec![var_expr.clone()],
  272. },
  273. Expression::Variable {
  274. loc: *loc,
  275. ty: slot_ty.clone(),
  276. var_no: new_length,
  277. },
  278. elem_ty.clone(),
  279. ns,
  280. ),
  281. },
  282. );
  283. let val = if *return_ty != Type::Void {
  284. let res_pos = vartab.temp_anonymous(&elem_ty);
  285. let expr = load_storage(
  286. loc,
  287. &elem_ty,
  288. Expression::Variable {
  289. loc: *loc,
  290. ty: elem_ty.clone(),
  291. var_no: entry_pos,
  292. },
  293. cfg,
  294. vartab,
  295. );
  296. cfg.add(
  297. vartab,
  298. Instr::Set {
  299. loc: *loc,
  300. res: res_pos,
  301. expr,
  302. },
  303. );
  304. Expression::Variable {
  305. loc: *loc,
  306. ty: elem_ty.clone(),
  307. var_no: res_pos,
  308. }
  309. } else {
  310. Expression::Undefined {
  311. ty: elem_ty.clone(),
  312. }
  313. };
  314. cfg.add(
  315. vartab,
  316. Instr::ClearStorage {
  317. ty: elem_ty,
  318. storage: Expression::Variable {
  319. loc: *loc,
  320. ty: slot_ty.clone(),
  321. var_no: entry_pos,
  322. },
  323. },
  324. );
  325. // set decrease length
  326. cfg.add(
  327. vartab,
  328. Instr::SetStorage {
  329. ty: slot_ty.clone(),
  330. value: Expression::Variable {
  331. loc: *loc,
  332. ty: slot_ty,
  333. var_no: new_length,
  334. },
  335. storage: var_expr,
  336. },
  337. );
  338. val
  339. }
  340. /// Push() method on array or bytes in storage
  341. pub fn array_push(
  342. loc: &pt::Loc,
  343. args: &[ast::Expression],
  344. cfg: &mut ControlFlowGraph,
  345. contract_no: usize,
  346. func: Option<&Function>,
  347. ns: &Namespace,
  348. vartab: &mut Vartable,
  349. opt: &Options,
  350. ) -> Expression {
  351. let storage = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  352. let mut ty = args[0].ty().storage_array_elem();
  353. let value = if args.len() > 1 {
  354. Some(expression(
  355. &args[1],
  356. cfg,
  357. contract_no,
  358. func,
  359. ns,
  360. vartab,
  361. opt,
  362. ))
  363. } else {
  364. ty.deref_any().default(ns)
  365. };
  366. if !ty.is_reference_type(ns) {
  367. ty = ty.deref_into();
  368. }
  369. let res = vartab.temp_anonymous(&ty);
  370. cfg.add(
  371. vartab,
  372. Instr::PushStorage {
  373. res,
  374. ty: ty.deref_any().clone(),
  375. storage,
  376. value,
  377. },
  378. );
  379. Expression::Variable {
  380. loc: *loc,
  381. ty,
  382. var_no: res,
  383. }
  384. }
  385. /// Pop() method on array or bytes in storage
  386. pub fn array_pop(
  387. loc: &pt::Loc,
  388. args: &[ast::Expression],
  389. return_ty: &Type,
  390. cfg: &mut ControlFlowGraph,
  391. contract_no: usize,
  392. func: Option<&Function>,
  393. ns: &Namespace,
  394. vartab: &mut Vartable,
  395. opt: &Options,
  396. ) -> Expression {
  397. let storage = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
  398. let ty = args[0].ty().storage_array_elem().deref_into();
  399. let res = if *return_ty != Type::Void {
  400. Some(vartab.temp_anonymous(&ty))
  401. } else {
  402. None
  403. };
  404. cfg.add(
  405. vartab,
  406. Instr::PopStorage {
  407. res,
  408. ty: ty.clone(),
  409. storage,
  410. },
  411. );
  412. if let Some(res) = res {
  413. Expression::Variable {
  414. loc: *loc,
  415. ty,
  416. var_no: res,
  417. }
  418. } else {
  419. Expression::Undefined { ty }
  420. }
  421. }