Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
fix(solc): traverse nodes iteratively (#800)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse authored Jan 17, 2022
1 parent a30a4ec commit 579311b
Showing 1 changed file with 55 additions and 30 deletions.
85 changes: 55 additions & 30 deletions ethers-solc/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
//! which is defined on a per source file basis.
use std::{
collections::{HashMap, VecDeque},
collections::{HashMap, HashSet, VecDeque},
path::{Path, PathBuf},
};

Expand Down Expand Up @@ -72,6 +72,21 @@ impl Graph {
&self.nodes[index]
}

/// Returns an iterator that yields all nodes of the dependency tree that the given node id
/// spans, starting with the node itself.
///
/// # Panics
///
/// if the `start` node id is not included in the graph
pub fn node_ids(&self, start: usize) -> impl Iterator<Item = usize> + '_ {
NodesIter::new(start, self)
}

/// Same as `Self::node_ids` but returns the actual `Node`
pub fn nodes(&self, start: usize) -> impl Iterator<Item = &Node> + '_ {
self.node_ids(start).map(move |idx| self.node(idx))
}

/// Returns all files together with their paths
pub fn into_sources(self) -> Sources {
self.nodes.into_iter().map(|node| (node.path, node.source)).collect()
Expand Down Expand Up @@ -241,32 +256,18 @@ impl Graph {
}

/// Filters incompatible versions from the `candidates`.
fn retain_compatible_versions(
&self,
idx: usize,
candidates: &mut Vec<&crate::SolcVersion>,
traversed: &mut std::collections::HashSet<(usize, usize)>,
) -> std::result::Result<(), String> {
let node = self.node(idx);
if let Some(ref req) = node.data.version_req {
candidates.retain(|v| req.matches(v.as_ref()));
}
for dep in self.imported_nodes(idx).iter().copied() {
// check for circular deps which would result in endless recursion SO here
// a circular dependency exists, if there was already a `dependency imports current
// node` relationship in the traversed path we skip this node
traversed.insert((idx, dep));
if traversed.contains(&(dep, idx)) {
tracing::warn!(
"Detected cyclic imports {} <-> {}",
utils::source_name(&self.nodes[idx].path, &self.root).display(),
utils::source_name(&self.nodes[dep].path, &self.root).display()
);
continue
fn retain_compatible_versions(&self, idx: usize, candidates: &mut Vec<&crate::SolcVersion>) {
let nodes: HashSet<_> = self.node_ids(idx).collect();
for node in nodes {
let node = self.node(node);
if let Some(ref req) = node.data.version_req {
candidates.retain(|v| req.matches(v.as_ref()));
}
if candidates.is_empty() {
// nothing to filter anymore
return
}
self.retain_compatible_versions(dep, candidates, traversed)?;
}
Ok(())
}

/// Ensures that all files are compatible with all of their imports.
Expand Down Expand Up @@ -304,11 +305,7 @@ impl Graph {
// walking through the node's dep tree and filtering the versions along the way
for idx in 0..self.num_input_files {
let mut candidates = all_versions.iter().collect::<Vec<_>>();
let mut traveresd = std::collections::HashSet::new();
if let Err(msg) = self.retain_compatible_versions(idx, &mut candidates, &mut traveresd)
{
errors.push(msg);
}
self.retain_compatible_versions(idx, &mut candidates);

if candidates.is_empty() && !erroneous_nodes.contains(&idx) {
let mut msg = String::new();
Expand Down Expand Up @@ -344,6 +341,34 @@ impl Graph {
}
}

/// An iterator over a node and its dependencies
#[derive(Debug)]
pub struct NodesIter<'a> {
/// stack of nodes
stack: VecDeque<usize>,
visited: HashSet<usize>,
graph: &'a Graph,
}

impl<'a> NodesIter<'a> {
fn new(start: usize, graph: &'a Graph) -> Self {
Self { stack: VecDeque::from([start]), visited: Default::default(), graph }
}
}

impl<'a> Iterator for NodesIter<'a> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
let node = self.stack.pop_front()?;

if self.visited.insert(node) {
// push the node's direct dependencies to the stack if we haven't visited it already
self.stack.extend(self.graph.imported_nodes(node).iter().copied());
}
Some(node)
}
}

/// Container type for solc versions and their compatible sources
#[cfg(all(feature = "svm", feature = "async"))]
#[derive(Debug)]
Expand Down

0 comments on commit 579311b

Please sign in to comment.