evm.rs 8.4 KB

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