Skip to content

Commit

Permalink
Merge pull request #596 from sbillig/depgraph
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig authored Nov 30, 2021
2 parents 9908db7 + b97e4f8 commit 6d08662
Show file tree
Hide file tree
Showing 67 changed files with 1,871 additions and 1,279 deletions.
31 changes: 29 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/analyzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ parking_lot_core = { version = "=0.8.0" } # used by salsa; version pinned for wa
indexmap = "1.6.2"
if_chain = "1.0.1"
smallvec = { version = "1.6.1", features = ["union"] }
petgraph = "0.6.0"

[dev-dependencies]
insta = "1.7.1"
Expand Down
8 changes: 6 additions & 2 deletions crates/analyzer/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ pub enum ValueMethod {
AbiEncode,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumString, AsRefStr, Hash, EnumIter)]
#[derive(
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, EnumString, AsRefStr, EnumIter,
)]
#[strum(serialize_all = "snake_case")]
pub enum GlobalFunction {
Keccak256,
Expand All @@ -33,7 +35,9 @@ impl ContractTypeMethod {
}
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumString, EnumIter, AsRefStr)]
#[derive(
Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, EnumString, EnumIter, AsRefStr,
)]
#[strum(serialize_all = "lowercase")]
pub enum GlobalObject {
Block,
Expand Down
24 changes: 17 additions & 7 deletions crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use fe_common::diagnostics::Label;
use fe_common::Span;
use fe_parser::ast;
use fe_parser::node::NodeId;
use indexmap::IndexMap;
use indexmap::{IndexMap, IndexSet};
use std::collections::HashMap;
use std::fmt;
use std::fmt::{Debug, Display};
Expand Down Expand Up @@ -195,6 +195,7 @@ impl Location {
pub struct FunctionBody {
pub expressions: IndexMap<NodeId, ExpressionAttributes>,
pub emits: IndexMap<NodeId, EventId>,
pub string_literals: IndexSet<String>, // for yulgen

// This is the id of the VarDecl TypeDesc node
pub var_decl_types: IndexMap<NodeId, FixedSize>,
Expand Down Expand Up @@ -262,7 +263,10 @@ impl fmt::Display for ExpressionAttributes {
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CallType {
BuiltinFunction(GlobalFunction),
BuiltinValueMethod(ValueMethod),
BuiltinValueMethod {
method: ValueMethod,
typ: Type,
},

// create, create2 (will be methods of the context struct soon)
BuiltinAssociatedFunction {
Expand All @@ -280,6 +284,10 @@ pub enum CallType {
class: Class,
method: FunctionId,
},
External {
contract: ContractId,
function: FunctionId,
},
Pure(FunctionId),
TypeConstructor(Type),
}
Expand All @@ -289,23 +297,25 @@ impl CallType {
use CallType::*;
match self {
BuiltinFunction(_)
| BuiltinValueMethod(_)
| BuiltinValueMethod { .. }
| TypeConstructor(_)
| BuiltinAssociatedFunction { .. } => None,
AssociatedFunction { function: id, .. } | ValueMethod { method: id, .. } | Pure(id) => {
Some(*id)
}
AssociatedFunction { function: id, .. }
| ValueMethod { method: id, .. }
| External { function: id, .. }
| Pure(id) => Some(*id),
}
}

pub fn function_name(&self, db: &dyn AnalyzerDb) -> String {
match self {
CallType::BuiltinFunction(f) => f.as_ref().to_string(),
CallType::BuiltinValueMethod(f) => f.as_ref().to_string(),
CallType::BuiltinValueMethod { method, .. } => method.as_ref().to_string(),
CallType::BuiltinAssociatedFunction { function, .. } => function.as_ref().to_string(),

CallType::AssociatedFunction { function: id, .. }
| CallType::ValueMethod { method: id, .. }
| CallType::External { function: id, .. }
| CallType::Pure(id) => id.name(db),
CallType::TypeConstructor(typ) => typ.to_string(),
}
Expand Down
15 changes: 13 additions & 2 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::context::{Analysis, FunctionBody};
use crate::errors::TypeError;
use crate::namespace::items::{
self, ContractFieldId, ContractId, EventId, FunctionId, GlobalId, IngotId, Item,
ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, GlobalId, IngotId,
Item, ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
};
use crate::namespace::types;
use fe_common::Span;
Expand Down Expand Up @@ -119,12 +119,21 @@ pub trait AnalyzerDb {
&self,
field: ContractFieldId,
) -> Analysis<Result<types::Type, TypeError>>;
#[salsa::cycle(queries::contracts::contract_dependency_graph_cycle)]
#[salsa::invoke(queries::contracts::contract_dependency_graph)]
fn contract_dependency_graph(&self, id: ContractId) -> DepGraphWrapper;
#[salsa::cycle(queries::contracts::contract_runtime_dependency_graph_cycle)]
#[salsa::invoke(queries::contracts::contract_runtime_dependency_graph)]
fn contract_runtime_dependency_graph(&self, id: ContractId) -> DepGraphWrapper;

// Function
#[salsa::invoke(queries::functions::function_signature)]
fn function_signature(&self, id: FunctionId) -> Analysis<Rc<types::FunctionSignature>>;
#[salsa::invoke(queries::functions::function_body)]
fn function_body(&self, id: FunctionId) -> Analysis<Rc<FunctionBody>>;
#[salsa::cycle(queries::functions::function_dependency_graph_cycle)]
#[salsa::invoke(queries::functions::function_dependency_graph)]
fn function_dependency_graph(&self, id: FunctionId) -> DepGraphWrapper;

// Struct
#[salsa::invoke(queries::structs::struct_type)]
Expand All @@ -142,6 +151,8 @@ pub trait AnalyzerDb {
fn struct_all_functions(&self, id: StructId) -> Rc<Vec<FunctionId>>;
#[salsa::invoke(queries::structs::struct_function_map)]
fn struct_function_map(&self, id: StructId) -> Analysis<Rc<IndexMap<String, FunctionId>>>;
#[salsa::invoke(queries::structs::struct_dependency_graph)]
fn struct_dependency_graph(&self, id: StructId) -> DepGraphWrapper;

// Event
#[salsa::invoke(queries::events::event_type)]
Expand Down
82 changes: 79 additions & 3 deletions crates/analyzer/src/db/queries/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::context::{AnalyzerContext, NamedThing};
use crate::db::{Analysis, AnalyzerDb};
use crate::errors;
use crate::namespace::items::{self, ContractFieldId, ContractId, EventId, FunctionId, Item};
use crate::namespace::items::{
self, ContractFieldId, ContractId, DepGraph, DepGraphWrapper, DepLocality, EventId, FunctionId,
Item, TypeDef,
};
use crate::namespace::scopes::ItemScope;
use crate::namespace::types;
use crate::namespace::types::{self, Contract, Struct, Type};
use crate::traversal::types::type_desc;
use fe_common::diagnostics::Label;
use fe_parser::ast;
use indexmap::map::{Entry, IndexMap};

use std::rc::Rc;

/// A `Vec` of every function defined in the contract, including duplicates and the init function.
Expand Down Expand Up @@ -277,3 +279,77 @@ pub fn contract_field_type(
diagnostics: Rc::new(scope.diagnostics),
}
}

pub fn contract_dependency_graph(db: &dyn AnalyzerDb, contract: ContractId) -> DepGraphWrapper {
// A contract depends on the types of its fields, and the things those types depend on.
// Note that this *does not* include the contract's public function graph.
// (See `contract_runtime_dependency_graph` below)

let fields = contract.fields(db);
let field_types = fields
.values()
.filter_map(|field| match field.typ(db).ok()? {
// We don't want
Type::Contract(Contract { id, .. }) => Some(Item::Type(TypeDef::Contract(id))),
Type::Struct(Struct { id, .. }) => Some(Item::Type(TypeDef::Struct(id))),
// TODO: when tuples can contain non-primitive items,
// we'll have to depend on tuple element types
_ => None,
})
.collect::<Vec<_>>();

let root = Item::Type(TypeDef::Contract(contract));
let mut graph = DepGraph::from_edges(
field_types
.iter()
.map(|item| (root, *item, DepLocality::Local)),
);

for item in field_types {
if let Some(subgraph) = item.dependency_graph(db) {
graph.extend(subgraph.all_edges())
}
}
DepGraphWrapper(Rc::new(graph))
}

pub fn contract_dependency_graph_cycle(
_db: &dyn AnalyzerDb,
_cycle: &[String],
_contract: &ContractId,
) -> DepGraphWrapper {
DepGraphWrapper(Rc::new(DepGraph::new()))
}

pub fn contract_runtime_dependency_graph(
db: &dyn AnalyzerDb,
contract: ContractId,
) -> DepGraphWrapper {
// This is the dependency graph of the (as yet imaginary) `__call__` function, which
// dispatches to the contract's public functions. This should be used when compiling
// the runtime object for a contract.

let root = Item::Type(TypeDef::Contract(contract));
let pub_fns = contract
.public_functions(db)
.values()
.map(|fun| (root, Item::Function(*fun), DepLocality::Local))
.collect::<Vec<_>>();

let mut graph = DepGraph::from_edges(pub_fns.iter());

for (_, item, _) in pub_fns {
if let Some(subgraph) = item.dependency_graph(db) {
graph.extend(subgraph.all_edges())
}
}
DepGraphWrapper(Rc::new(graph))
}

pub fn contract_runtime_dependency_graph_cycle(
_db: &dyn AnalyzerDb,
_cycle: &[String],
_contract: &ContractId,
) -> DepGraphWrapper {
DepGraphWrapper(Rc::new(DepGraph::new()))
}
Loading

0 comments on commit 6d08662

Please sign in to comment.