Skip to content

Commit

Permalink
Merge pull request #467 from sbillig/yul-context
Browse files Browse the repository at this point in the history
Move static string and created-contracts collection from the analyzer the yulgen stage
  • Loading branch information
g-r-a-n-t authored Jun 24, 2021
2 parents b0caf12 + c4e50c7 commit 417b4d1
Show file tree
Hide file tree
Showing 114 changed files with 165 additions and 416 deletions.
8 changes: 1 addition & 7 deletions analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use fe_parser::ast as fe;
use fe_parser::node::{Node, NodeId};

use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;
use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::rc::Rc;
Expand Down Expand Up @@ -52,14 +52,10 @@ pub struct ContractAttributes {
pub init_function: Option<FunctionAttributes>,
/// Events that have been defined by the user.
pub events: Vec<EventDef>,
/// Static strings that the contract defines
pub string_literals: BTreeSet<String>,
/// Structs that have been defined by the user
pub structs: Vec<Struct>,
/// External contracts that may be called from within this contract.
pub external_contracts: Vec<Contract>,
/// Names of contracts that have been created inside of this contract.
pub created_contracts: BTreeSet<String>,
}

impl From<Shared<ContractScope>> for ContractAttributes {
Expand Down Expand Up @@ -119,10 +115,8 @@ impl From<Shared<ContractScope>> for ContractAttributes {
.values()
.map(|event| event.to_owned())
.collect::<Vec<EventDef>>(),
string_literals: scope.borrow().string_defs.clone(),
structs,
external_contracts,
created_contracts: scope.borrow().created_contracts.to_owned(),
}
}
}
Expand Down
17 changes: 1 addition & 16 deletions analyzer/src/namespace/scopes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::namespace::events::EventDef;
use crate::namespace::types::{FixedSize, Type};
use std::cell::RefCell;
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Shared<T> = Rc<RefCell<T>>;
Expand Down Expand Up @@ -37,8 +37,6 @@ pub struct ContractScope {
pub event_defs: BTreeMap<String, EventDef>,
pub field_defs: BTreeMap<String, ContractFieldDef>,
pub function_defs: BTreeMap<String, ContractFunctionDef>,
pub string_defs: BTreeSet<String>,
pub created_contracts: BTreeSet<String>,
num_fields: usize,
}

Expand Down Expand Up @@ -114,9 +112,7 @@ impl ContractScope {
function_defs: BTreeMap::new(),
event_defs: BTreeMap::new(),
field_defs: BTreeMap::new(),
string_defs: BTreeSet::new(),
interface: vec![],
created_contracts: BTreeSet::new(),
num_fields: 0,
}))
}
Expand Down Expand Up @@ -192,17 +188,6 @@ impl ContractScope {
}
}
}

/// Add a static string definition to the scope.
pub fn add_string(&mut self, value: &str) {
self.string_defs.insert(value.to_owned());
}

/// Add the name of another contract that has been created within this
/// contract.
pub fn add_created_contract(&mut self, name: &str) {
self.created_contracts.insert(name.to_owned());
}
}

impl BlockScope {
Expand Down
29 changes: 4 additions & 25 deletions analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub fn expr(
fe::Expr::Call { .. } => expr_call(scope, context, exp),
fe::Expr::List { elts } => expr_list(scope, context, elts, expected_type.as_array()),
fe::Expr::Tuple { .. } => expr_tuple(scope, context, exp, expected_type.as_tuple()),
fe::Expr::Str(_) => expr_str(scope, exp),
fe::Expr::Str(_) => expr_str(exp),
fe::Expr::Unit => Ok(ExpressionAttributes::new(Type::unit(), Location::Value)),
}?;

Expand Down Expand Up @@ -312,17 +312,8 @@ fn expr_name(
unreachable!()
}

fn expr_str(
scope: Shared<BlockScope>,
exp: &Node<fe::Expr>,
) -> Result<ExpressionAttributes, FatalError> {
fn expr_str(exp: &Node<fe::Expr>) -> Result<ExpressionAttributes, FatalError> {
if let fe::Expr::Str(string) = &exp.kind {
scope
.borrow()
.contract_scope()
.borrow_mut()
.add_string(&string);

return Ok(ExpressionAttributes::new(
Type::String(FeString {
max_size: string.len(),
Expand Down Expand Up @@ -1261,16 +1252,10 @@ fn expr_call_type_attribute(
report_circular_dependency(context, ContractTypeMethod::Create2.to_string());
}

if matches!(
if !matches!(
(&arg_attributes[0].typ, &arg_attributes[1].typ),
(Type::Base(Base::Numeric(_)), Type::Base(Base::Numeric(_)))
) {
scope
.borrow()
.contract_scope()
.borrow_mut()
.add_created_contract(&contract.name);
} else {
context.fancy_error(
"function `create2` expects numeric parameters",
vec![Label::primary(args.span, "invalid argument")],
Expand All @@ -1289,13 +1274,7 @@ fn expr_call_type_attribute(
report_circular_dependency(context, ContractTypeMethod::Create.to_string());
}

if matches!(&arg_attributes[0].typ, Type::Base(Base::Numeric(_))) {
scope
.borrow()
.contract_scope()
.borrow_mut()
.add_created_contract(&contract.name);
} else {
if !matches!(&arg_attributes[0].typ, Type::Base(Base::Numeric(_))) {
context.fancy_error(
"function `create` expects numeric parameter",
vec![Label::primary(args.span, "invalid argument")],
Expand Down
23 changes: 23 additions & 0 deletions compiler/src/yul/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::yul::AnalyzerContext;
use indexmap::IndexSet;

// This is contract context, but it's used all over so it has a short name.
pub struct Context<'a> {
pub analysis: &'a AnalyzerContext,

/// String literals used in the contract
pub string_literals: IndexSet<String>,

/// Names of contracts that have been created inside of this contract.
pub created_contracts: IndexSet<String>,
}

impl<'a> Context<'a> {
pub fn new(analysis: &'a AnalyzerContext) -> Self {
Self {
analysis,
string_literals: IndexSet::new(),
created_contracts: IndexSet::new(),
}
}
}
13 changes: 7 additions & 6 deletions compiler/src/yul/mappers/assignments.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
use crate::yul::mappers::expressions;
use crate::yul::operations::data as data_operations;
use fe_analyzer::context::{Context, Location};
use crate::yul::Context;
use fe_analyzer::context::Location;
use fe_analyzer::namespace::types::FixedSize;
use fe_parser::ast as fe;
use fe_parser::node::Node;
use std::convert::TryFrom;
use yultsur::*;

/// Builds a Yul statement from a Fe assignment.
pub fn assign(context: &Context, stmt: &Node<fe::FuncStmt>) -> yul::Statement {
pub fn assign(context: &mut Context, stmt: &Node<fe::FuncStmt>) -> yul::Statement {
if let fe::FuncStmt::Assign { target, value } = &stmt.kind {
if let (Some(target_attributes), Some(value_attributes)) = (
context.get_expression(target),
context.get_expression(value),
context.analysis.get_expression(target),
context.analysis.get_expression(value),
) {
let target = expressions::expr(&context, target);
let value = expressions::expr(&context, value);
let target = expressions::expr(context, target);
let value = expressions::expr(context, value);

let typ =
FixedSize::try_from(target_attributes.typ.to_owned()).expect("invalid attributes");
Expand Down
55 changes: 31 additions & 24 deletions compiler/src/yul/mappers/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,65 @@
use crate::yul::constructor;
use crate::yul::mappers::functions;
use crate::yul::runtime;
use fe_analyzer::context::Context;
use crate::yul::{AnalyzerContext, Context};
use fe_common::utils::keccak;
use fe_parser::ast as fe;
use fe_parser::node::Node;
use std::collections::HashMap;
use yultsur::*;

/// Builds a Yul object from a Fe contract.
pub fn contract_def(
context: &Context,
analysis: &AnalyzerContext,
stmt: &Node<fe::Contract>,
created_contracts: Vec<yul::Object>,
contracts: &HashMap<String, yul::Object>,
) -> yul::Object {
let fe::Contract { name, body, .. } = &stmt.kind;
let contract_name = &name.kind;
let mut init_function = None;
let mut user_functions = vec![];

let mut context = Context::new(analysis);

// map user defined functions
for stmt in body.iter() {
match stmt {
fe::ContractStmt::Function(def) => {
let attributes = context
.get_function(def)
.expect("missing function attributes");
let yulfn = functions::func_def(&mut context, def);

if def.kind.name.kind == "__init__" {
init_function =
Some((functions::func_def(context, def), attributes.param_types()))
let attributes = analysis
.get_function(def)
.expect("missing function attributes");
init_function = Some((yulfn, attributes.param_types()))
} else {
user_functions.push(functions::func_def(context, def))
user_functions.push(yulfn)
}
}
fe::ContractStmt::Event(_) => {}
}
}

// build the set of functions needed during runtime
let runtime_functions = runtime::build_with_abi_dispatcher(context, stmt);
let runtime_functions = runtime::build_with_abi_dispatcher(&context, stmt);

// build data objects for static strings (also for constants in the future)
let data = if let Some(attributes) = context.get_contract(stmt) {
attributes
.string_literals
.clone()
.into_iter()
.map(|val| yul::Data {
name: keccak::full(val.as_bytes()),
value: val,
})
.collect::<Vec<_>>()
} else {
vec![]
};
let data = context
.string_literals
.iter()
.map(|val| yul::Data {
name: keccak::full(val.as_bytes()),
value: val.clone(),
})
.collect::<Vec<_>>();

// Map the set of created contract names to their Yul objects so they can be
// included in the Yul contract that deploys them.
let created_contracts = context
.created_contracts
.iter()
.map(|contract_name| contracts[contract_name].clone())
.collect::<Vec<_>>();

// create the runtime object
let runtime_object = yul::Object {
Expand Down Expand Up @@ -80,7 +87,7 @@ pub fn contract_def(
// user-defined functions can be called from `__init__`.
let (constructor_code, constructor_objects) =
if let Some((init_func, init_params)) = init_function {
let init_runtime_functions = [runtime::build(context, stmt), user_functions].concat();
let init_runtime_functions = [runtime::build(&context, stmt), user_functions].concat();
let constructor_code = constructor::build_with_init(
contract_name,
init_func,
Expand Down
10 changes: 6 additions & 4 deletions compiler/src/yul/mappers/declarations.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::yul::mappers::expressions;
use crate::yul::names;
use fe_analyzer::context::Context;
use crate::yul::{names, Context};
use fe_analyzer::namespace::types::{FeSized, FixedSize};
use fe_parser::ast as fe;
use fe_parser::node::Node;
use yultsur::*;

/// Builds a Yul statement from a Fe variable declaration
pub fn var_decl(context: &Context, stmt: &Node<fe::FuncStmt>) -> yul::Statement {
let decl_type = context.get_declaration(stmt).expect("missing attributes");
pub fn var_decl(context: &mut Context, stmt: &Node<fe::FuncStmt>) -> yul::Statement {
let decl_type = context
.analysis
.get_declaration(stmt)
.expect("missing attributes");

if let fe::FuncStmt::VarDecl { target, value, .. } = &stmt.kind {
let target = names::var_name(var_decl_name(&target.kind));
Expand Down
Loading

0 comments on commit 417b4d1

Please sign in to comment.