external_functions.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::sema::ast::{DestructureField, Expression, Namespace, Statement};
  3. use crate::sema::Recurse;
  4. use indexmap::IndexSet;
  5. use solang_parser::pt;
  6. #[derive(Default)]
  7. struct CallList {
  8. pub solidity: IndexSet<usize>,
  9. pub yul: IndexSet<usize>,
  10. }
  11. /// After generating the AST for a contract, we should have a list of
  12. /// all the functions a contract calls in `all_functions`. This should
  13. /// include any libraries and global functions
  14. pub fn add_external_functions(contract_no: usize, ns: &mut Namespace) {
  15. let mut call_list = CallList::default();
  16. for var in &ns.contracts[contract_no].variables {
  17. if let Some(init) = &var.initializer {
  18. init.recurse(&mut call_list, check_expression);
  19. }
  20. }
  21. for function_no in ns.contracts[contract_no].all_functions.keys() {
  22. let func = &ns.functions[*function_no];
  23. for stmt in &func.body {
  24. stmt.recurse(&mut call_list, check_statement);
  25. }
  26. }
  27. // we've now collected all the functions which are called.
  28. while !call_list.solidity.is_empty() || !call_list.yul.is_empty() {
  29. let mut new_call_list = CallList::default();
  30. for function_no in &call_list.solidity {
  31. let func = &ns.functions[*function_no];
  32. for stmt in &func.body {
  33. stmt.recurse(&mut new_call_list, check_statement);
  34. }
  35. }
  36. // add functions to contract functions list
  37. for function_no in &call_list.solidity {
  38. if ns.functions[*function_no].loc != pt::Loc::Builtin {
  39. ns.contracts[contract_no]
  40. .all_functions
  41. .insert(*function_no, usize::MAX);
  42. }
  43. }
  44. for yul_function_no in &call_list.yul {
  45. ns.contracts[contract_no]
  46. .yul_functions
  47. .push(*yul_function_no);
  48. }
  49. call_list.solidity.clear();
  50. call_list.yul.clear();
  51. for function_no in &new_call_list.solidity {
  52. if !ns.contracts[contract_no]
  53. .all_functions
  54. .contains_key(function_no)
  55. {
  56. call_list.solidity.insert(*function_no);
  57. }
  58. }
  59. for yul_func_no in &new_call_list.yul {
  60. ns.contracts[contract_no].yul_functions.push(*yul_func_no);
  61. }
  62. }
  63. // now that we have the final list of functions, we can populate the list
  64. // of events this contract emits
  65. let mut send_events = Vec::new();
  66. for function_no in ns.contracts[contract_no].all_functions.keys() {
  67. let func = &ns.functions[*function_no];
  68. for event_no in &func.emits_events {
  69. if !send_events.contains(event_no) {
  70. send_events.push(*event_no);
  71. }
  72. }
  73. }
  74. ns.contracts[contract_no].sends_events = send_events;
  75. }
  76. fn check_expression(expr: &Expression, call_list: &mut CallList) -> bool {
  77. if let Expression::InternalFunction { function_no, .. } = expr {
  78. call_list.solidity.insert(*function_no);
  79. }
  80. true
  81. }
  82. fn check_statement(stmt: &Statement, call_list: &mut CallList) -> bool {
  83. match stmt {
  84. Statement::VariableDecl(_, _, _, Some(expr)) => {
  85. expr.recurse(call_list, check_expression);
  86. }
  87. Statement::VariableDecl(_, _, _, None) => (),
  88. Statement::If(_, _, cond, _, _) => {
  89. cond.recurse(call_list, check_expression);
  90. }
  91. Statement::For {
  92. cond: Some(cond), ..
  93. } => {
  94. cond.recurse(call_list, check_expression);
  95. }
  96. Statement::For { cond: None, .. } => (),
  97. Statement::DoWhile(_, _, _, cond) | Statement::While(_, _, cond, _) => {
  98. cond.recurse(call_list, check_expression);
  99. }
  100. Statement::Expression(_, _, expr) => {
  101. expr.recurse(call_list, check_expression);
  102. }
  103. Statement::Delete(_, _, expr) => {
  104. expr.recurse(call_list, check_expression);
  105. }
  106. Statement::Destructure(_, fields, expr) => {
  107. // This is either a list or internal/external function call
  108. expr.recurse(call_list, check_expression);
  109. for field in fields {
  110. if let DestructureField::Expression(expr) = field {
  111. expr.recurse(call_list, check_expression);
  112. }
  113. }
  114. }
  115. Statement::Return(_, exprs) => {
  116. for e in exprs {
  117. e.recurse(call_list, check_expression);
  118. }
  119. }
  120. Statement::TryCatch(_, _, try_catch) => {
  121. try_catch.expr.recurse(call_list, check_expression);
  122. }
  123. Statement::Emit { args, .. } => {
  124. for e in args {
  125. e.recurse(call_list, check_expression);
  126. }
  127. }
  128. Statement::Block { .. }
  129. | Statement::Break(_)
  130. | Statement::Continue(_)
  131. | Statement::Underscore(_) => (),
  132. Statement::Assembly(inline_assembly, _) => {
  133. for func_no in inline_assembly.functions.start..inline_assembly.functions.end {
  134. call_list.yul.insert(func_no);
  135. }
  136. }
  137. }
  138. true
  139. }