contract.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // SPDX-License-Identifier: Apache-2.0
  2. use path_slash::PathExt;
  3. use rayon::prelude::*;
  4. use solang::{codegen, file_resolver::FileResolver, parse_and_resolve, Target};
  5. use std::{
  6. ffi::OsStr,
  7. fs::{read_dir, File},
  8. io::{self, Read},
  9. path::{Path, PathBuf},
  10. };
  11. #[test]
  12. fn solana_contracts() -> io::Result<()> {
  13. contract_tests("tests/contract_testcases/solana", Target::Solana)
  14. }
  15. #[test]
  16. fn substrate_contracts() -> io::Result<()> {
  17. contract_tests(
  18. "tests/contract_testcases/substrate",
  19. Target::default_substrate(),
  20. )
  21. }
  22. #[test]
  23. fn evm_contracts() -> io::Result<()> {
  24. contract_tests("tests/contract_testcases/evm", Target::EVM)
  25. }
  26. fn contract_tests(file_path: &str, target: Target) -> io::Result<()> {
  27. let path = PathBuf::from(file_path);
  28. recurse_directory(path, target)
  29. }
  30. fn recurse_directory(path: PathBuf, target: Target) -> io::Result<()> {
  31. let mut entries = Vec::new();
  32. for entry in read_dir(path)? {
  33. let path = entry?.path();
  34. if path.is_dir() {
  35. recurse_directory(path, target)?;
  36. } else if let Some(ext) = path.extension() {
  37. if ext.to_string_lossy() == "sol" {
  38. entries.push(path);
  39. }
  40. }
  41. }
  42. entries.into_par_iter().for_each(|entry| {
  43. parse_file(entry, target).unwrap();
  44. });
  45. Ok(())
  46. }
  47. fn parse_file(path: PathBuf, target: Target) -> io::Result<()> {
  48. let mut cache = FileResolver::new();
  49. let filename = add_file(&mut cache, &path, target)?;
  50. let mut ns = parse_and_resolve(OsStr::new(&filename), &mut cache, target);
  51. if !ns.diagnostics.any_errors() {
  52. // codegen all the contracts
  53. codegen::codegen(
  54. &mut ns,
  55. &codegen::Options {
  56. math_overflow_check: false,
  57. opt_level: codegen::OptimizationLevel::Default,
  58. ..Default::default()
  59. },
  60. );
  61. }
  62. #[cfg(windows)]
  63. {
  64. for file in &mut ns.files {
  65. let filename = file.path.to_slash_lossy().to_string();
  66. file.path = PathBuf::from(filename);
  67. }
  68. }
  69. if !ns.diagnostics.any_errors() {
  70. // let's try and emit
  71. match ns.target {
  72. Target::Solana | Target::Substrate { .. } => {
  73. for contract in &ns.contracts {
  74. if contract.instantiable {
  75. let _ = contract.emit(&ns, &Default::default());
  76. }
  77. }
  78. }
  79. Target::EVM => {
  80. // not implemented yet
  81. }
  82. }
  83. }
  84. let mut path = path;
  85. path.set_extension("dot");
  86. let generated_dot = ns.dotgraphviz();
  87. // uncomment the next three lines to regenerate the test data
  88. // use std::io::Write;
  89. // let mut file = File::create(&path)?;
  90. // file.write_all(generated_dot.as_bytes())?;
  91. let mut file = File::open(&path)?;
  92. let mut test_dot = String::new();
  93. file.read_to_string(&mut test_dot)?;
  94. // The dot files may have had their end of lines mangled on Windows
  95. let test_dot = test_dot.replace("\r\n", "\n");
  96. pretty_assertions::assert_eq!(generated_dot, test_dot);
  97. Ok(())
  98. }
  99. fn add_file(cache: &mut FileResolver, path: &Path, target: Target) -> io::Result<String> {
  100. let mut file = File::open(path)?;
  101. let mut source = String::new();
  102. file.read_to_string(&mut source)?;
  103. // make sure the path uses unix file separators, this is what the dot file uses
  104. let filename = path.to_slash_lossy();
  105. println!("Parsing {filename} for {target}");
  106. // The files may have had their end of lines mangled on Windows
  107. cache.set_file_contents(&filename, source.replace("\r\n", "\n"));
  108. for line in source.lines() {
  109. if line.starts_with("import") {
  110. let start = line.find('"').unwrap();
  111. let end = line.rfind('"').unwrap();
  112. let file = &line[start + 1..end];
  113. if file != "solana" {
  114. let mut import_path = path.parent().unwrap().to_path_buf();
  115. import_path.push(file);
  116. println!("adding import {}", import_path.display());
  117. add_file(cache, &import_path, target)?;
  118. }
  119. }
  120. }
  121. Ok(filename.to_string())
  122. }