contract.rs 4.7 KB

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