From c2f969fcadc0530649f0fb3f1fd204bae699a593 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sat, 27 May 2023 20:04:23 +0000 Subject: [PATCH] feat: config refactor, read evm_version from foundry.toml --- foundry.toml | 1 + huff_cli/src/huffc.rs | 6 +- huff_core/src/lib.rs | 44 +++++---- huff_parser/src/lib.rs | 12 +-- huff_utils/src/config.rs | 71 +++++++++++++++ huff_utils/src/evm_version.rs | 4 +- huff_utils/src/files.rs | 164 +--------------------------------- huff_utils/src/foundry.rs | 131 +++++++++++++++++++++++++++ huff_utils/src/lib.rs | 9 ++ huff_utils/src/remapper.rs | 3 + 10 files changed, 250 insertions(+), 195 deletions(-) create mode 100644 huff_utils/src/config.rs create mode 100644 huff_utils/src/foundry.rs create mode 100644 huff_utils/src/remapper.rs diff --git a/foundry.toml b/foundry.toml index 7a356b80..d199b11d 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,3 +3,4 @@ optimizer = true remappings = [ "examples/=huff-examples/", ] +evm_version = "paris" \ No newline at end of file diff --git a/huff_cli/src/huffc.rs b/huff_cli/src/huffc.rs index f0605bc0..77d2cc79 100644 --- a/huff_cli/src/huffc.rs +++ b/huff_cli/src/huffc.rs @@ -17,6 +17,7 @@ use huff_tests::{ HuffTester, }; use huff_utils::{ + config::HuffConfig, file_provider::FileSystemFileProvider, prelude::{ export_interfaces, gen_sol_interfaces, str_to_bytes32, unpack_files, AstSpan, BytecodeRes, @@ -190,6 +191,9 @@ fn main() { .collect() }); + // Parse external config + let config = HuffConfig::new("./"); + // Parse the EVM version let evm_version = EVMVersion::from(cli.evm_version); @@ -212,7 +216,7 @@ fn main() { }; let compiler: Compiler = Compiler { - evm_version: &evm_version, + huff_config: config, sources: Arc::clone(&sources), output, alternative_main: cli.alternative_main.clone(), diff --git a/huff_core/src/lib.rs b/huff_core/src/lib.rs index e40e0151..921db6dd 100644 --- a/huff_core/src/lib.rs +++ b/huff_core/src/lib.rs @@ -11,8 +11,10 @@ use huff_parser::*; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] use huff_utils::wasm::IntoParallelIterator; use huff_utils::{ + config::HuffConfig, file_provider::{FileProvider, FileSystemFileProvider, InMemoryFileProvider}, prelude::*, + remapper::Remapper, time, }; #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] @@ -59,9 +61,9 @@ pub(crate) mod cache; /// ); /// ``` #[derive(Debug, Clone)] -pub struct Compiler<'a, 'l> { - /// The EVM version to compile for - pub evm_version: &'l EVMVersion, +pub struct Compiler<'a> { + /// Huff Config + pub huff_config: HuffConfig, /// The location of the files to compile pub sources: Arc>, /// The output location @@ -84,11 +86,11 @@ pub struct Compiler<'a, 'l> { pub file_provider: Arc>, } -impl<'a, 'l> Compiler<'a, 'l> { +impl<'a> Compiler<'a> { /// Public associated function to instantiate a new compiler. #[allow(clippy::too_many_arguments)] pub fn new( - evm_version: &'l EVMVersion, + huff_config: HuffConfig, sources: Arc>, output: Option, alternative_main: Option, @@ -102,7 +104,7 @@ impl<'a, 'l> Compiler<'a, 'l> { Compiler::init_tracing_subscriber(Some(vec![tracing::Level::INFO.into()])); } Self { - evm_version, + huff_config, sources, output, alternative_main, @@ -120,7 +122,7 @@ impl<'a, 'l> Compiler<'a, 'l> { /// map. #[allow(clippy::too_many_arguments)] pub fn new_in_memory( - evm_version: &'l EVMVersion, + evm_version: &EVMVersion, sources: Arc>, file_sources: HashMap, alternative_main: Option, @@ -132,8 +134,9 @@ impl<'a, 'l> Compiler<'a, 'l> { if cfg!(feature = "verbose") || verbose { Compiler::init_tracing_subscriber(Some(vec![tracing::Level::INFO.into()])); } + let huff_config = HuffConfig::from_evm_version(evm_version.clone()); Self { - evm_version, + huff_config, sources, output: None, alternative_main, @@ -226,7 +229,7 @@ impl<'a, 'l> Compiler<'a, 'l> { let recursed_file_sources: Vec, Arc>> = files .into_par_iter() .map(|v| { - Self::recurse_deps(v, &Remapper::new("./"), self.file_provider.clone()) + Self::recurse_deps(v, self.huff_config.clone(), self.file_provider.clone()) }) .collect(); @@ -307,13 +310,7 @@ impl<'a, 'l> Compiler<'a, 'l> { let recursed_file_sources: Vec, Arc>> = files .into_par_iter() - .map(|f| { - Self::recurse_deps( - f, - &huff_utils::files::Remapper::new("./"), - self.file_provider.clone(), - ) - }) + .map(|f| Self::recurse_deps(f, self.huff_config.clone(), self.file_provider.clone())) .collect(); // Collect Recurse Deps errors and try to resolve to the first one @@ -358,7 +355,8 @@ impl<'a, 'l> Compiler<'a, 'l> { tracing::info!(target: "core", "└─ TOKEN COUNT: {}", tokens.len()); // Parser incantation - let mut parser = Parser::new(tokens, Some(file.path.clone())); + let mut parser = + Parser::new(self.huff_config.clone(), tokens, Some(file.path.clone())); // Parse into an AST let parse_res = parser.parse().map_err(CompilerError::ParserError); @@ -396,7 +394,7 @@ impl<'a, 'l> Compiler<'a, 'l> { tracing::info!(target: "core", "└─ TOKEN COUNT: {}", tokens.len()); // Parser incantation - let mut parser = Parser::new(tokens, Some(file.path.clone())); + let mut parser = Parser::new(self.huff_config.clone(), tokens, Some(file.path.clone())); // Parse into an AST let parse_res = parser.parse().map_err(CompilerError::ParserError); @@ -408,7 +406,7 @@ impl<'a, 'l> Compiler<'a, 'l> { // Primary Bytecode Generation let mut cg = Codegen::new(); let main_bytecode = match Codegen::generate_main_bytecode( - self.evm_version, + &self.huff_config.evm_version, &contract, self.alternative_main.clone(), ) { @@ -436,7 +434,7 @@ impl<'a, 'l> Compiler<'a, 'l> { let inputs = self.get_constructor_args(); let (constructor_bytecode, has_custom_bootstrap) = match Codegen::generate_constructor_bytecode( - self.evm_version, + &self.huff_config.evm_version, &contract, self.alternative_constructor.clone(), ) { @@ -515,7 +513,7 @@ impl<'a, 'l> Compiler<'a, 'l> { /// Recurses file dependencies pub fn recurse_deps( fs: Arc, - remapper: &Remapper, + config: HuffConfig, reader: Arc>, ) -> Result, Arc> { tracing::debug!(target: "core", "RECURSING DEPENDENCIES FOR {}", fs.path); @@ -544,7 +542,7 @@ impl<'a, 'l> Compiler<'a, 'l> { .into_iter() .map(|mut import| { // Check for foundry toml remappings - match remapper.remap(&import) { + match config.remap(&import) { Some(remapped) => { tracing::debug!(target: "core", "REMAPPED IMPORT PATH \"{}\"", import); import = remapped; @@ -575,7 +573,7 @@ impl<'a, 'l> Compiler<'a, 'l> { // Now that we have all the file sources, we have to recurse and get their source file_sources = file_sources .into_par_iter() - .map(|inner_fs| match Self::recurse_deps(Arc::clone(&inner_fs), remapper, reader.clone()) { + .map(|inner_fs| match Self::recurse_deps(Arc::clone(&inner_fs), config.clone(), reader.clone()) { Ok(new_fs) => new_fs, Err(e) => { tracing::error!(target: "core", "NESTED DEPENDENCY RESOLUTION FAILED: \"{:?}\"", e); diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs index 5eba06ab..96deceb6 100644 --- a/huff_parser/src/lib.rs +++ b/huff_parser/src/lib.rs @@ -6,9 +6,10 @@ use huff_utils::{ ast::*, + config::HuffConfig, error::*, - files, prelude::{bytes32_to_string, hash_bytes, str_to_bytes32, Span}, + remapper::Remapper, token::{Token, TokenKind}, types::*, }; @@ -17,6 +18,8 @@ use regex::Regex; /// The Parser #[derive(Debug, Clone)] pub struct Parser { + /// Config + pub config: HuffConfig, /// Vector of the tokens pub tokens: Vec, /// Current position @@ -27,16 +30,13 @@ pub struct Parser { pub base: Option, /// A collection of current spans pub spans: Vec, - /// Our remapper - pub remapper: files::Remapper, } impl Parser { /// Public associated function that instantiates a Parser. - pub fn new(tokens: Vec, base: Option) -> Self { + pub fn new(config: HuffConfig, tokens: Vec, base: Option) -> Self { let initial_token = tokens.get(0).unwrap().clone(); - let remapper = files::Remapper::new("./"); - Self { tokens, cursor: 0, current_token: initial_token, base, spans: vec![], remapper } + Self { config, tokens, cursor: 0, current_token: initial_token, base, spans: vec![] } } /// Resets the current token and cursor to the first token in the parser's token vec diff --git a/huff_utils/src/config.rs b/huff_utils/src/config.rs new file mode 100644 index 00000000..762ac136 --- /dev/null +++ b/huff_utils/src/config.rs @@ -0,0 +1,71 @@ +use std::{collections::HashMap, fs::read_to_string, path::PathBuf}; + +use crate::{evm_version::EVMVersion, foundry::FoundryConfig, remapper::Remapper}; + +/// Stores external configuration options, such as remappings and evm version. + +#[derive(Debug, Default, Clone)] +pub struct HuffConfig { + pub base_dir: String, + pub evm_version: EVMVersion, + pub remappings: HashMap, +} + +impl HuffConfig { + pub fn new(root: impl AsRef) -> Self { + let base_dir = root.as_ref().to_string(); + + // Parse foundry config and remappings + let foundry_config = FoundryConfig::new(&base_dir); + let file_remappings = remappings_from_file(&base_dir); + + let mut remappings = HashMap::::new(); + remappings.extend(file_remappings); + remappings.extend(foundry_config.remappings); + + let evm_version = EVMVersion::from(foundry_config.evm_version); + + HuffConfig { base_dir, evm_version, remappings } + } + + pub fn from_evm_version(evm_version: EVMVersion) -> Self { + HuffConfig { base_dir: "".to_string(), evm_version, remappings: HashMap::new() } + } +} + +impl Remapper for HuffConfig { + /// Tries to replace path segments in a string with our remappings + fn remap(&self, path: &str) -> Option { + let mut path = path.to_string(); + for (k, v) in self.remappings.iter() { + if path.starts_with(k) { + tracing::debug!(target: "parser", "found key {} and value {}", k, v); + path = path.replace(k, v); + return Some(format!("{}{path}", self.base_dir)) + } + } + None + } +} + +// Read remappings from remappings.txt +fn remappings_from_file(root: &str) -> HashMap { + let mut remappings: HashMap = HashMap::new(); + let remappings_file = PathBuf::new().join(root).join("remappings.txt"); + if remappings_file.is_file() { + let content = read_to_string(remappings_file).map_err(|err| err.to_string()).unwrap(); + + let rem_lines = content.split('\n').collect::>(); + let rem = rem_lines + .iter() + .filter(|l| l != &&"") + .map(|l| l.split_once('=')) + .collect::>>(); + rem.iter().for_each(|pair| { + if let Some((lib, path)) = pair { + remappings.insert(lib.to_string(), path.to_string()); + } + }); + } + remappings +} diff --git a/huff_utils/src/evm_version.rs b/huff_utils/src/evm_version.rs index 44e1a2af..91151ab5 100644 --- a/huff_utils/src/evm_version.rs +++ b/huff_utils/src/evm_version.rs @@ -4,7 +4,7 @@ use std::cmp::PartialOrd; /// /// Determines which features will be available when compiling. -#[derive(Debug, PartialEq, PartialOrd)] +#[derive(Clone, Debug, PartialEq, PartialOrd)] pub enum SupportedEVMVersions { /// Introduced prevrandao, disallow difficulty opcode (does not affect codegen) Paris, @@ -12,7 +12,7 @@ pub enum SupportedEVMVersions { Shanghai, } -#[derive(Debug)] +#[derive(Debug, Clone)] /// EVM Version pub struct EVMVersion { version: SupportedEVMVersions, diff --git a/huff_utils/src/files.rs b/huff_utils/src/files.rs index 56a12785..e796fae5 100644 --- a/huff_utils/src/files.rs +++ b/huff_utils/src/files.rs @@ -1,13 +1,5 @@ -use itertools::Itertools; use serde::{Deserialize, Serialize}; -use std::{ - cell::Ref, - collections::HashMap, - fs, - io::{BufReader, Read}, - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{cell::Ref, path::PathBuf, sync::Arc}; use uuid::Uuid; #[allow(clippy::to_string_in_format_args)] @@ -43,160 +35,6 @@ impl<'a> FullFileSource<'a> { } } -/// A wrapper for dealing with Remappings -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct Remapper { - /// The remappings - pub remappings: HashMap, - /// The base directory - pub base_dir: String, -} - -impl Remapper { - /// Extracts remappings from configuration files. - /// - /// Currently only supports `foundry.toml` remapping definitions. - pub fn new(root: impl AsRef) -> Self { - let mut inner = HashMap::::new(); - - // Gracefully parse remappings from foundry.toml - Remapper::from_foundry(root.as_ref(), &mut inner); - - // And from remappings.txt - Remapper::from_file(root.as_ref(), &mut inner); - - // Return the constructed remappings - Self { remappings: inner, base_dir: root.as_ref().to_string() } - } - - /// Helper to break apart a remapping gracefully - pub fn split(remapping: &str) -> Option<(String, String)> { - let mut split = remapping.splitn(2, '='); - match split.next() { - Some(from) => split.next().map(|to| (from.to_string(), to.to_string())), - None => None, - } - } - - /// Parse foundry toml remappings - pub fn from_foundry(root: &str, inner: &mut HashMap) { - // Look for a `foundry.toml` file in the current directory. - let path = Path::new(root).join("foundry.toml"); - - match fs::File::open(&path) { - Ok(f) => { - // Open the buffered reader and read foundry.toml - let mut data = String::new(); - let mut br = BufReader::new(f); - - // Gracefully read foundry.toml - if let Err(e) = br.read_to_string(&mut data) { - tracing::warn!(target: "parser", "Failed to read \"foundry.toml\" file contents!\nError: {:?}", e); - return - } - - // Parse the foundry.toml file as toml - let toml = if let Ok(t) = data.parse::() { - t - } else { - tracing::warn!(target: "parser", "\"foundry.toml\" incorrectly formatted!"); - return - }; - - // Parse the toml as a map - let toml_map = toml.as_table().cloned().unwrap_or_else(toml::value::Map::new); - - // Transform the mappings into profiles - let profiles = toml_map - .iter() - .filter_map(|p| p.1.as_table()) - .collect::>>(); - let unwrapped_profiles = profiles - .iter() - .flat_map(|t| t.values().collect_vec()) - .collect::>(); - - // Extract the inner tables from each profile - let inner_tables = unwrapped_profiles - .iter() - .filter_map(|t| t.as_table()) - .collect::>>(); - let unwrapped_inner_tables = inner_tables - .iter() - .flat_map(|t| { - t.into_iter().filter(|m| m.0.eq("remappings")).map(|m| m.1).collect_vec() - }) - .collect::>(); - - // Extract mappings that are arrays - let arr_mappings = unwrapped_inner_tables - .iter() - .filter_map(|t| t.as_array()) - .collect::>>(); - let unwrapped_mappings = - arr_mappings.iter().cloned().flatten().collect::>(); - - // Filter the remappings as strings - let remapping_strings = - unwrapped_mappings.iter().filter_map(|t| t.as_str()).collect::>(); - - // For each remapping string, try to split it and insert it into the remappings - remapping_strings.iter().for_each(|remapping| { - match Remapper::split(remapping) { - Some((from, to)) => { - inner.insert(from, to); - } - None => tracing::warn!(target: "parser", "Failed to split remapping using \"=\" at \"{}\" in \"{}\"!", remapping, path.to_string_lossy()), - } - }); - } - Err(e) => { - tracing::warn!(target: "parser", "Foundry.toml not found in specified \"{}\"", root); - tracing::warn!(target: "parser", "{:?}", e); - } - } - } - - /// Get remappings from a remappings.txt file - pub fn from_file(root: &str, inner: &mut HashMap) { - let mut remappings: HashMap = HashMap::new(); - let remappings_file = PathBuf::new().join(root).join("remappings.txt"); - if remappings_file.is_file() { - let content = - fs::read_to_string(remappings_file).map_err(|err| err.to_string()).unwrap(); - - let rem_lines = content.split('\n').collect::>(); - let rem = rem_lines - .iter() - .filter(|l| l != &&"") - .map(|l| l.split_once('=')) - .collect::>>(); - rem.iter().for_each(|pair| { - if let Some((lib, path)) = pair { - remappings.insert(lib.to_string(), path.to_string()); - } - }); - - inner.extend(remappings); - } - } -} - -impl Remapper { - /// Tries to replace path segments in a string with our remappings - pub fn remap(&self, path: &str) -> Option { - let mut path = path.to_string(); - for (k, v) in self.remappings.iter() { - if path.starts_with(k) { - tracing::debug!(target: "parser", "found key {} and value {}", k, v); - path = path.replace(k, v); - return Some(format!("{}{path}", self.base_dir)) - } - } - None - } -} - /// File Encapsulation #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct FileSource { diff --git a/huff_utils/src/foundry.rs b/huff_utils/src/foundry.rs new file mode 100644 index 00000000..aa3b6495 --- /dev/null +++ b/huff_utils/src/foundry.rs @@ -0,0 +1,131 @@ +use std::{ + collections::HashMap, + fs::File, + io::{BufReader, Read}, + path::{Path, PathBuf}, +}; + +use itertools::Itertools; + +/// Foundry.toml reading support +pub struct FoundryConfig { + path: PathBuf, + // The settings we care about + pub remappings: HashMap, + // The evm version + pub evm_version: Option, +} + +impl FoundryConfig { + pub fn new(base_dir: &str) -> Self { + let mut foundry_toml_parser = Self { + path: Path::new(base_dir).join("foundry.toml"), + remappings: HashMap::::new(), + evm_version: None, + }; + + // Parse the toml file and return it + foundry_toml_parser.parse_toml(); + foundry_toml_parser + } + + /// Helper to break apart a remapping gracefully + fn split_remappings(remapping: &str) -> Option<(String, String)> { + let mut split = remapping.splitn(2, '='); + match split.next() { + Some(from) => split.next().map(|to| (from.to_string(), to.to_string())), + None => None, + } + } + + fn parse_toml(&mut self) { + match File::open(&self.path) { + Ok(f) => { + // Open the buffered reader and read foundry.toml + let mut data = String::new(); + let mut br = BufReader::new(f); + + // Gracefully read foundry.toml + if let Err(e) = br.read_to_string(&mut data) { + tracing::warn!(target: "utils", "Failed to read \"foundry.toml\" file contents!\nError: {:?}", e); + return + } + + // Parse toml to get the remappings + let toml = if let Ok(t) = data.parse::() { + t + } else { + tracing::warn!(target: "utils", "\"foundry.toml\" incorrectly formatted!"); + return + }; + + // Parse the toml as a map + let toml_map = toml.as_table().cloned().unwrap_or_else(toml::value::Map::new); + + // Transform the mappings into profiles + let profiles = toml_map + .iter() + .filter_map(|p| p.1.as_table()) + .collect::>>(); + let unwrapped_profiles = profiles + .iter() + .flat_map(|t| t.values().collect_vec()) + .collect::>(); + + // Extract the inner tables from each profile + let inner_tables = unwrapped_profiles + .iter() + .filter_map(|t| t.as_table()) + .collect::>>(); + + let unwrapped_inner_remappings = inner_tables + .iter() + .flat_map(|t| { + t.into_iter() + .filter(|m| m.0.eq("remappings") || m.0.eq("evm_version")) + .map(|m| m.1) + .collect_vec() + }) + .collect::>(); + + // Strings will be the evm version + let evm_versions = unwrapped_inner_remappings + .iter() + .filter_map(|t| t.as_str()) + .collect::>(); + self.evm_version = evm_versions.first().map(|s| s.to_string()); + + // Extract mappings that are arrays + let arr_mappings = unwrapped_inner_remappings + .iter() + .filter_map(|t| t.as_array()) + .collect::>>(); + let unwrapped_mappings = + arr_mappings.iter().cloned().flatten().collect::>(); + + // Filter the remappings as strings + let remapping_strings = + unwrapped_mappings.iter().filter_map(|t| t.as_str()).collect::>(); + + // For each remapping string, try to split it and insert it into the remappings + remapping_strings.iter().for_each(|remapping| { + match FoundryConfig::split_remappings(remapping) { + Some((from, to)) => { + self.remappings.insert(from, to); + } + None => tracing::warn!(target: "utils", "Failed to split remapping using \"=\" at \"{}\" in \"{}\"!", remapping, "fixme"), + } + }); + } + + Err(e) => { + tracing::warn!(target: "utils", "Foundry.toml not found"); + // tracing::warn!(target: "utils", "Foundry.toml not found in specified \"{}\"", + // self.path.to_string()); + tracing::warn!(target: "utils", "{:?}", e); + } + } + } + + pub fn parse_remappings() {} +} diff --git a/huff_utils/src/lib.rs b/huff_utils/src/lib.rs index 3dd3fc87..cfce0ad4 100644 --- a/huff_utils/src/lib.rs +++ b/huff_utils/src/lib.rs @@ -57,6 +57,15 @@ pub mod wasm; /// EVM Version Module pub mod evm_version; +/// Huff Config Module +pub mod config; + +/// File Remapping Module +pub mod remapper; + +/// Foundry.toml helper module +mod foundry; + /// Prelude wraps common utilities. pub mod prelude { pub use crate::{ diff --git a/huff_utils/src/remapper.rs b/huff_utils/src/remapper.rs new file mode 100644 index 00000000..31252e82 --- /dev/null +++ b/huff_utils/src/remapper.rs @@ -0,0 +1,3 @@ +pub trait Remapper { + fn remap(&self, path: &str) -> Option; +}