Skip to content

Commit

Permalink
Salsa-based analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig committed Aug 9, 2021
1 parent c8e58b5 commit 7c1cf74
Show file tree
Hide file tree
Showing 209 changed files with 21,429 additions and 82,885 deletions.
111 changes: 111 additions & 0 deletions Cargo.lock

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

171 changes: 115 additions & 56 deletions crates/abi/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,76 +1,135 @@
use crate::elements::{Component, Contract, Event, EventField, JsonAbi, ModuleAbis};
use crate::errors::AbiError;
use fe_analyzer::context::Context;
use fe_parser::ast as fe;
use crate::elements::{
Component, Contract, Event, EventField, FuncInput, FuncOutput, FuncType, Function, JsonAbi,
ModuleAbis,
};
use crate::AbiError;
use fe_analyzer::namespace::items::{ContractId, ModuleId};
use fe_analyzer::namespace::types;
use fe_analyzer::AnalyzerDb;

/// Parse a map of contract ABIs from the input `module`.
pub fn module(context: &Context, module: &fe::Module) -> Result<ModuleAbis, AbiError> {
pub fn module(db: &dyn AnalyzerDb, module: ModuleId) -> Result<ModuleAbis, AbiError> {
module
.body
.all_contracts(db)
.iter()
.try_fold(ModuleAbis::new(), |mut abis, stmt| {
if let fe::ModuleStmt::Contract(contract) = &stmt {
let name = &contract.kind.name.kind;
if abis
.insert(name.to_string(), contract_def(context, &contract.kind.body))
.is_some()
{
return Err(AbiError::DuplicateContractDefinition(name.to_string()));
}
};

.try_fold(ModuleAbis::new(), |mut abis, contract| {
if abis
.insert(contract.name(db), contract_def(db, *contract))
.is_some()
{
return Err(AbiError::DuplicateContractDefinition(contract.name(db)));
}
Ok(abis)
})
}

fn contract_def(context: &Context, body: &[fe::ContractStmt]) -> Contract {
body.iter().fold(Contract::new(), |mut contract, stmt| {
match &stmt {
fe::ContractStmt::Function(def) => {
let attributes = context
.get_function(def)
.expect("missing function attributes");

if attributes.is_public {
contract.functions.push(attributes.to_owned().into())
}
fn contract_def(db: &dyn AnalyzerDb, contract: ContractId) -> Contract {
let events = contract
.events(db)
.iter()
.map(|(name, eventid)| {
let attributes = eventid.typ(db);
Event {
name: name.to_owned(),
typ: "event".to_string(),
fields: attributes
.fields
.iter()
.map(|field| {
let typ = field.typ.clone().expect("event field type error");
EventField {
name: field.name.to_owned(),
typ: typ.abi_json_name(),
indexed: field.is_indexed,
components: components(db, &typ),
}
})
.collect(),
anonymous: false,
}
fe::ContractStmt::Event(def) => {
let attributes = context.get_event(def).expect("missing function attributes");
})
.collect();

let functions = contract
.functions(db)
.iter()
.filter_map(|(name, func)| {
if func.is_public(db) {
let sig = func.signature(db);
let (name, func_type) = match name.as_str() {
"__init__" => ("".to_owned(), FuncType::Constructor),
_ => (name.to_owned(), FuncType::Function),
};

let inputs = sig
.params
.iter()
.map(|param| {
let typ = param.typ.clone().expect("function parameter type error");

let event = Event {
name: attributes.name.to_owned(),
typ: "event".to_string(),
fields: attributes
.fields
.iter()
.enumerate()
.map(|(index, (name, typ))| EventField {
name: name.to_owned(),
FuncInput {
name: param.name.to_owned(),
typ: typ.abi_json_name(),
indexed: attributes.indexed_fields.contains(&index),
components: typ
.abi_components()
.iter()
.map(|component| Component::from(component.to_owned()))
.collect(),
})
.collect(),
anonymous: false,
components: components(db, &typ),
}
})
.collect();

let return_type = sig.return_type.clone().expect("function return type error");
let outputs = if return_type.is_unit() {
vec![]
} else {
vec![FuncOutput {
name: "".to_string(),
typ: return_type.abi_json_name(),
components: components(db, &return_type),
}]
};

contract.events.push(event);
Some(Function {
name,
typ: func_type,
inputs,
outputs,
})
} else {
None
}
}
})
.collect();

Contract { events, functions }
}

contract
})
fn components(db: &dyn AnalyzerDb, typ: &types::FixedSize) -> Vec<Component> {
match typ {
types::FixedSize::Struct(types::Struct { id, .. }) => id
.fields(db)
.iter()
.map(|(name, field_id)| Component {
name: name.to_owned(),
typ: field_id
.typ(db)
.expect("struct field type error")
.abi_json_name(),
})
.collect(),
types::FixedSize::Tuple(types::Tuple { items }) => items
.iter()
.enumerate()
.map(|(index, item)| Component {
name: format!("item{}", index),
typ: item.abi_json_name(),
})
.collect(),
_ => vec![],
}
}

#[cfg(test)]
mod tests {
use crate::builder;
use fe_common::files::SourceFileId;
use fe_analyzer::Db;
use fe_parser::{grammar::module::parse_module, parse_code_chunk};

#[test]
Expand All @@ -89,9 +148,9 @@ mod tests {
let module = parse_code_chunk(parse_module, contract)
.expect("unable to build module AST")
.kind;
let context =
fe_analyzer::analyze(&module, SourceFileId(0)).expect("failed to analyze source");
let abis = builder::module(&context, &module).expect("unable to build ABI");
let db = Db::default();
let module_id = fe_analyzer::analyze(&db, module).expect("failed to analyze source");
let abis = builder::module(&db, module_id).expect("unable to build ABI");

if let Some(abi) = abis.get("Foo") {
// event
Expand Down
Loading

0 comments on commit 7c1cf74

Please sign in to comment.