evm.rs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // SPDX-License-Identifier: Apache-2.0
  2. use rayon::prelude::*;
  3. use solang::{file_resolver::FileResolver, parse_and_resolve, sema::ast, Target};
  4. use std::{ffi::OsStr, fs, path::Path};
  5. use walkdir::WalkDir;
  6. fn test_solidity(src: &str) -> ast::Namespace {
  7. let mut cache = FileResolver::new();
  8. cache.set_file_contents("test.sol", src.to_string());
  9. let ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::EVM);
  10. ns.print_diagnostics_in_plain(&cache, false);
  11. ns
  12. }
  13. #[test]
  14. fn address() {
  15. let ns = test_solidity(
  16. "
  17. contract address_tester {
  18. function encode_const() public returns (address) {
  19. return 0x52908400098527886E0F7030069857D2E4169EE7;
  20. }
  21. function test_arg(address foo) public {
  22. assert(foo == 0x27b1fdb04752bbc536007a920d24acb045561c26);
  23. // this literal is a number
  24. int x = 0x27b1fdb047_52bbc536007a920d24acb045561C26;
  25. assert(int(foo) == x);
  26. }
  27. function allones() public returns (address) {
  28. return address(1);
  29. }
  30. }",
  31. );
  32. assert!(!ns.diagnostics.any_errors());
  33. }
  34. #[test]
  35. fn try_catch() {
  36. let ns = test_solidity(
  37. r##"
  38. contract b {
  39. int32 x;
  40. constructor(int32 a) public {
  41. x = a;
  42. }
  43. function get_x(int32 t) public returns (int32) {
  44. if (t == 0) {
  45. revert("cannot be zero");
  46. }
  47. return x * t;
  48. }
  49. }
  50. contract c {
  51. b x;
  52. constructor() public {
  53. x = new b(102);
  54. }
  55. function test() public returns (int32) {
  56. int32 state = 0;
  57. try x.get_x(0) returns (int32 l) {
  58. state = 1;
  59. } catch Error(string err) {
  60. if (err == "cannot be zero") {
  61. state = 2;
  62. } else {
  63. state = 3;
  64. }
  65. } catch (bytes ) {
  66. state = 4;
  67. }
  68. return state;
  69. }
  70. }"##,
  71. );
  72. assert!(!ns.diagnostics.any_errors());
  73. }
  74. #[test]
  75. fn selfdestruct() {
  76. let ns = test_solidity(
  77. r##"
  78. contract other {
  79. function goaway(address payable recipient) public returns (bool) {
  80. selfdestruct(recipient);
  81. }
  82. }
  83. contract c {
  84. other o;
  85. function step1() public {
  86. o = new other{value: 511}();
  87. }
  88. function step2() public {
  89. bool foo = o.goaway(payable(address(this)));
  90. }
  91. }"##,
  92. );
  93. assert!(!ns.diagnostics.any_errors());
  94. }
  95. #[test]
  96. fn eth_builtins() {
  97. let ns = test_solidity(
  98. r#"
  99. contract testing {
  100. function test_address() public view returns (uint256 ret) {
  101. assembly {
  102. let a := address()
  103. ret := a
  104. }
  105. }
  106. function test_balance() public view returns (uint256 ret) {
  107. assembly {
  108. let a := address()
  109. ret := balance(a)
  110. }
  111. }
  112. function test_selfbalance() public view returns (uint256 ret) {
  113. assembly {
  114. let a := selfbalance()
  115. ret := a
  116. }
  117. }
  118. function test_caller() public view returns (uint256 ret) {
  119. assembly {
  120. let a := caller()
  121. ret := a
  122. }
  123. }
  124. function test_callvalue() public view returns (uint256 ret) {
  125. assembly {
  126. let a := callvalue()
  127. ret := a
  128. }
  129. }
  130. function test_extcodesize() public view returns (uint256 ret) {
  131. assembly {
  132. let a := address()
  133. ret := extcodesize(a)
  134. }
  135. }
  136. }
  137. "#,
  138. );
  139. assert!(!ns.diagnostics.any_errors());
  140. }
  141. #[test]
  142. fn ethereum_solidity_tests() {
  143. let error_matcher = regex::Regex::new(r"// ----\r?\n// \w+Error( \d+)?:").unwrap();
  144. let entries = WalkDir::new(
  145. Path::new(env!("CARGO_MANIFEST_DIR"))
  146. .join("testdata/solidity/test/libsolidity/semanticTests"),
  147. )
  148. .into_iter()
  149. .chain(
  150. WalkDir::new(
  151. Path::new(env!("CARGO_MANIFEST_DIR"))
  152. .join("testdata/solidity/test/libsolidity/syntaxTests"),
  153. )
  154. .into_iter(),
  155. );
  156. let errors: usize = entries
  157. .par_bridge()
  158. .filter_map(|e| {
  159. let entry = e.unwrap();
  160. let file_name = entry.file_name().to_string_lossy();
  161. // FIXME: max_depth_reached_4.sol causes a stack overflow in resolve_expression.rs
  162. // FIXNE: others listed explicitly cause panics and need fixing
  163. if !file_name.ends_with("max_depth_reached_4.sol")
  164. && !file_name.ends_with("invalid_utf8_sequence.sol")
  165. && !file_name.ends_with("basefee_berlin_function.sol")
  166. && !file_name.ends_with("inline_assembly_embedded_function_call.sol")
  167. && !file_name.ends_with("linkersymbol_function.sol")
  168. && !file_name.ends_with("370_shift_constant_left_excessive_rvalue.sol")
  169. && !file_name.ends_with("use_msize_with_optimizer.sol")
  170. && !file_name.ends_with("two_stack_slots.sol")
  171. && !file_name.ends_with("two_stack_slot_access.sol")
  172. && !file_name.ends_with("difficulty_disallowed_function_pre_paris.sol")
  173. && !file_name.ends_with("difficulty_reserved_post_paris.sol")
  174. && !file_name.ends_with("mapping_with_names_nested_7.sol")
  175. && file_name.ends_with(".sol")
  176. {
  177. Some(entry)
  178. } else {
  179. None
  180. }
  181. })
  182. .map(|entry| {
  183. let path = entry.path().parent().unwrap();
  184. let source = fs::read_to_string(entry.path()).unwrap();
  185. let expect_error = error_matcher.is_match(&source);
  186. let (mut cache, names) = set_file_contents(&source, path);
  187. cache.add_import_path(path).unwrap();
  188. let errors: usize = names
  189. .iter()
  190. .map(|name| {
  191. let ns = parse_and_resolve(OsStr::new(&name), &mut cache, Target::EVM);
  192. if ns.diagnostics.any_errors() {
  193. if !expect_error {
  194. println!("file: {}", entry.path().display());
  195. ns.print_diagnostics_in_plain(&cache, false);
  196. 1
  197. } else {
  198. 0
  199. }
  200. } else if expect_error {
  201. println!("file: {}", entry.path().display());
  202. println!("expecting error, none found");
  203. 1
  204. } else {
  205. 0
  206. }
  207. })
  208. .sum();
  209. errors
  210. })
  211. .sum();
  212. assert_eq!(errors, 1155);
  213. }
  214. fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {
  215. let mut cache = FileResolver::new();
  216. let mut name = "test.sol".to_owned();
  217. let mut names = Vec::new();
  218. let mut contents = String::new();
  219. let source_delimiter = regex::Regex::new(r"==== Source: (.*) ====").unwrap();
  220. let external_source_delimiter = regex::Regex::new(r"==== ExternalSource: (.*) ====").unwrap();
  221. let equals = regex::Regex::new("([a-zA-Z0-9_]+)=(.*)").unwrap();
  222. for line in source.lines() {
  223. if let Some(cap) = source_delimiter.captures(line) {
  224. if !contents.is_empty() {
  225. cache.set_file_contents(&name, contents);
  226. names.push(name);
  227. }
  228. name = cap.get(1).unwrap().as_str().to_owned();
  229. if name == "////" {
  230. name = "test.sol".to_owned();
  231. }
  232. contents = String::new();
  233. } else if let Some(cap) = external_source_delimiter.captures(line) {
  234. let mut name = cap.get(1).unwrap().as_str().to_owned();
  235. if let Some(cap) = equals.captures(&name) {
  236. let mut ext = path.to_path_buf();
  237. ext.push(cap.get(2).unwrap().as_str());
  238. name = cap.get(1).unwrap().as_str().to_owned();
  239. let source = fs::read_to_string(ext).unwrap();
  240. cache.set_file_contents(&name, source);
  241. }
  242. // else rely on file resolver to import stuff
  243. } else {
  244. contents.push_str(line);
  245. contents.push('\n');
  246. }
  247. }
  248. if !contents.is_empty() {
  249. cache.set_file_contents(&name, contents);
  250. names.push(name);
  251. }
  252. (cache, names)
  253. }