Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate a JSON abi from a .sw file #343

Merged
merged 32 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e34f93b
first commit
Oct 29, 2021
c24ea42
Pass around a JsonABI object.
Oct 29, 2021
3faff5a
Creating the JSON abi output.
Oct 29, 2021
a81acc8
Complete abi json functionality.
Nov 15, 2021
4b6f055
Merge with master.
Nov 16, 2021
2fab502
Merge remote-tracking branch 'origin/master' into emilyaherbert-322-2…
Nov 16, 2021
4c651da
format
Nov 16, 2021
878bf93
Undo the mistakes of past. i.e. type inference means that the JsonABI…
Nov 17, 2021
80c48c6
More.
Nov 19, 2021
d14ce57
Print json to outfile.
Nov 19, 2021
7fcfbe0
Merge remote-tracking branch 'origin/master' into emilyaherbert-322-2…
Nov 19, 2021
786d008
Actually output JSON.
Nov 22, 2021
f9a554e
Delete unused function.
Nov 22, 2021
75e545b
JSON captures components field.
Nov 22, 2021
09db90f
Merge remote-tracking branch 'origin/master' into emilyaherbert-322-2…
Nov 23, 2021
f86c33d
Basic testing infra.
Nov 23, 2021
82f1002
Actual oracle values.
Nov 23, 2021
851a408
Format.
Nov 23, 2021
0c80746
Split compile_asm into compile_ast.
Nov 23, 2021
038b7d8
Working on merge with master.
Nov 30, 2021
bc861bc
Finish merge.
Nov 30, 2021
7a9200c
nits
Nov 30, 2021
218bc2c
Fix bad merge.
Nov 30, 2021
2c042f0
Update oracles.
Nov 30, 2021
c7f20bb
Missing oracle files.
Nov 30, 2021
0ee5b08
Merge with master.
Dec 10, 2021
8d27323
fmt
Dec 10, 2021
3aeafb2
rename function.
Dec 10, 2021
3dd12ec
fix bug
Dec 10, 2021
d9ec3c3
Fix manifest_dir bug.
Dec 13, 2021
5ecfdc8
Merge with master.
Dec 13, 2021
213c025
add missing oracle files
Dec 14, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions core_lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ sha2 = "0.9"
structopt = { version = "0.3", default-features = false, optional = true }
thiserror = "1.0"
uuid-b64 = "0.1"
line-col = "0.2"
source-span = "2.4"
core-types = { path = "../core-types" }
emilyaherbert marked this conversation as resolved.
Show resolved Hide resolved
lazy_static = "1.4"


Expand Down
61 changes: 61 additions & 0 deletions core_lang/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ use crate::parser::Rule;
use crate::span::Span;
use crate::style::{to_screaming_snake_case, to_snake_case, to_upper_camel_case};
use crate::type_engine::{IntegerBits, TypeInfo};
use line_col::LineColLookup;
use source_span::{
fmt::{Formatter, Style},
Position,
};
use std::fmt;
use thiserror::Error;

Expand Down Expand Up @@ -169,6 +174,34 @@ impl<'sc> CompileWarning<'sc> {
self.span.end_pos().line_col().into(),
)
}

pub fn format(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
let input = self.span.input();
let chars = input.chars().map(|x| -> Result<_, ()> { Ok(x) });

let metrics = source_span::DEFAULT_METRICS;
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);

for c in buffer.iter() {
let _ = c.unwrap(); // report eventual errors.
}

let (start_pos, end_pos) = self.span();
let lookup = LineColLookup::new(input);
let (start_line, start_col) = lookup.get(start_pos);
let (end_line, end_col) = lookup.get(end_pos - 1);

let err_start = Position::new(start_line - 1, start_col - 1);
let err_end = Position::new(end_line - 1, end_col - 1);
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
fmt.add(
err_span,
Some(self.to_friendly_warning_string()),
Style::Warning,
);

fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -952,4 +985,32 @@ impl<'sc> CompileError<'sc> {
self.internal_span().end_pos().line_col().into(),
)
}

pub fn format(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
let input = self.internal_span().input();
let chars = input.chars().map(Result::<_, String>::Ok);

let metrics = source_span::DEFAULT_METRICS;
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);

for c in buffer.iter() {
let _ = c.unwrap(); // report eventual errors.
}

let (start_pos, end_pos) = self.span();
let lookup = LineColLookup::new(input);
let (start_line, start_col) = lookup.get(start_pos);
let (end_line, end_col) = lookup.get(if end_pos == 0 { 0 } else { end_pos - 1 });

let err_start = Position::new(start_line - 1, start_col - 1);
let err_end = Position::new(end_line - 1, end_col - 1);
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
fmt.add(
err_span,
Some(self.to_friendly_error_string()),
Style::Error,
);

fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
}
}
46 changes: 42 additions & 4 deletions core_lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::{asm_generation::compile_ast_to_asm, error::*};
pub use asm_generation::{AbstractInstructionSet, FinalizedAsm, HllAsmSet};
pub use build_config::BuildConfig;
use control_flow_analysis::{ControlFlowGraph, Graph};
use core_types::{Function, JsonABI};
use pest::iterators::Pair;
use pest::Parser;
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -132,17 +133,36 @@ pub fn parse<'sc>(
pub enum CompilationResult<'sc> {
Success {
asm: FinalizedAsm<'sc>,
json_abi: Vec<Function>,
emilyaherbert marked this conversation as resolved.
Show resolved Hide resolved
warnings: Vec<CompileWarning<'sc>>,
},
Library {
exports: LibraryExports<'sc>,
json_abi: Vec<Function>,
warnings: Vec<CompileWarning<'sc>>,
},
Failure {
warnings: Vec<CompileWarning<'sc>>,
errors: Vec<CompileError<'sc>>,
},
}

pub enum CompileASTsResult<'sc> {
Success {
contract_ast: Option<TypedParseTree<'sc>>,
script_ast: Option<TypedParseTree<'sc>>,
predicate_ast: Option<TypedParseTree<'sc>>,
library_exports: LibraryExports<'sc>,
json_abi: Vec<Function>,
dead_code_graph: ControlFlowGraph<'sc>,
warnings: Vec<CompileWarning<'sc>>,
},
Failure {
warnings: Vec<CompileWarning<'sc>>,
errors: Vec<CompileError<'sc>>,
},
}

pub enum BytecodeCompilationResult<'sc> {
Success {
bytes: Vec<u8>,
Expand Down Expand Up @@ -222,6 +242,7 @@ pub(crate) fn compile_inner_dependency<'sc>(
return err(warnings, errors);
}
}

let library_exports: LibraryExports = {
let res: Vec<_> = parse_tree
.library_exports
Expand Down Expand Up @@ -252,6 +273,7 @@ pub(crate) fn compile_inner_dependency<'sc>(
}
exports
};

// look for return path errors
for tree in &library_exports.trees {
let graph = ControlFlowGraph::construct_return_path_graph(tree);
Expand Down Expand Up @@ -345,13 +367,13 @@ pub fn compile_to_asm<'sc>(
exports
};

// If there are errors, display them now before performing control flow analysis.
// It is necessary that the syntax tree is well-formed for control flow analysis
// to be correct.
if !errors.is_empty() {
return CompilationResult::Failure { errors, warnings };
}

let mut json_abi = vec![];
json_abi.append(&mut parse_json_abi(&contract_ast));

// perform control flow analysis on each branch
let (script_warnings, script_errors) =
perform_control_flow_analysis(&script_ast, TreeType::Script, &mut dead_code_graph);
Expand Down Expand Up @@ -424,18 +446,22 @@ pub fn compile_to_asm<'sc>(
match (predicate_asm, contract_asm, script_asm, library_exports) {
(Some(pred), None, None, o) if o.trees.is_empty() => CompilationResult::Success {
asm: pred,
json_abi,
warnings,
},
(None, Some(contract), None, o) if o.trees.is_empty() => CompilationResult::Success {
asm: contract,
json_abi,
warnings,
},
(None, None, Some(script), o) if o.trees.is_empty() => CompilationResult::Success {
asm: script,
json_abi,
warnings,
},
(None, None, None, o) if !o.trees.is_empty() => CompilationResult::Library {
warnings,
json_abi,
exports: o,
},
(None, None, None, o) if o.trees.is_empty() => {
Expand All @@ -444,13 +470,14 @@ pub fn compile_to_asm<'sc>(
// Default to compiling an empty library if there is no code or invalid state
_ => unimplemented!(
"Multiple contracts, libraries, scripts, or predicates in a single file are \
unsupported."
unsupported."
),
}
} else {
CompilationResult::Failure { errors, warnings }
}
}

pub fn compile_to_bytecode<'sc>(
input: &'sc str,
initial_namespace: &Namespace<'sc>,
Expand All @@ -461,6 +488,7 @@ pub fn compile_to_bytecode<'sc>(
CompilationResult::Success {
mut asm,
mut warnings,
..
} => {
let mut asm_res = asm.to_bytecode();
warnings.append(&mut asm_res.warnings);
Expand All @@ -483,6 +511,7 @@ pub fn compile_to_bytecode<'sc>(
CompilationResult::Library {
warnings,
exports: _exports,
..
} => BytecodeCompilationResult::Library { warnings },
}
}
Expand Down Expand Up @@ -527,6 +556,15 @@ fn perform_control_flow_analysis_on_library_exports<'sc>(
(warnings, errors)
}

fn parse_json_abi(ast: &Option<TypedParseTree>) -> JsonABI {
match ast {
Some(TypedParseTree::Contract { abi_entries, .. }) => {
abi_entries.iter().map(|x| x.parse_json_abi()).collect()
}
_ => vec![],
}
}

// strategy: parse top level things
// and if we encounter a function body or block, recursively call this function and build
// sub-nodes
Expand Down
22 changes: 22 additions & 0 deletions core_lang/src/parse_tree/declaration/function_declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::span::Span;
use crate::style::is_snake_case;
use crate::type_engine::TypeInfo;
use crate::{CodeBlock, Ident, Rule};
use core_types::{Function, Property};
use pest::iterators::Pair;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -187,6 +188,27 @@ impl<'sc> FunctionDeclaration<'sc> {
errors,
)
}

pub fn parse_json_abi(&self) -> Function {
Function {
name: self.name.primary_name.to_string(),
type_field: "function".to_string(),
inputs: self
.parameters
.iter()
.map(|x| Property {
name: x.name.primary_name.to_string(),
type_field: x.r#type.friendly_type_str(),
components: None,
})
.collect(),
outputs: vec![Property {
name: "".to_string(),
type_field: self.return_type.friendly_type_str(),
components: None,
}],
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down
45 changes: 43 additions & 2 deletions core_lang/src/semantic_analysis/ast_node/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use super::impl_trait::Mode;
use super::{
IsConstant, TypedCodeBlock, TypedExpression, TypedExpressionVariant, TypedReturnStatement,
};
use crate::control_flow_analysis::ControlFlowGraph;
use crate::parse_tree::*;
use crate::semantic_analysis::Namespace;
use crate::span::Span;
use crate::type_engine::*;
use crate::ControlFlowGraph;
use crate::{build_config::BuildConfig, error::*, Ident};

use core_types::{Function, Property};
use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet};

Expand Down Expand Up @@ -212,6 +214,14 @@ impl OwnedTypedStructField {
span: span.clone(),
}
}

pub fn parse_json_abi(&self) -> Property {
Property {
name: self.name.clone(),
type_field: self.r#type.friendly_type_str(),
components: self.r#type.parse_json_abi(),
}
}
}

impl TypedStructField<'_> {
Expand Down Expand Up @@ -282,6 +292,16 @@ pub struct OwnedTypedEnumVariant {
pub(crate) tag: usize,
}

impl OwnedTypedEnumVariant {
pub fn parse_json_abi(&self) -> Property {
emilyaherbert marked this conversation as resolved.
Show resolved Hide resolved
Property {
name: self.name.clone(),
type_field: self.r#type.friendly_type_str(),
components: self.r#type.parse_json_abi(),
}
}
}

#[derive(Clone, Debug)]
pub struct TypedVariableDeclaration<'sc> {
pub(crate) name: Ident<'sc>,
Expand Down Expand Up @@ -410,6 +430,27 @@ impl<'sc> TypedFunctionDeclaration<'sc> {
errors,
)
}

pub fn parse_json_abi(&self) -> Function {
Function {
name: self.name.primary_name.to_string(),
type_field: "function".to_string(),
inputs: self
.parameters
.iter()
.map(|x| Property {
name: x.name.primary_name.to_string(),
type_field: x.r#type.friendly_type_str(),
components: x.r#type.parse_json_abi(),
})
.collect(),
outputs: vec![Property {
name: "".to_string(),
emilyaherbert marked this conversation as resolved.
Show resolved Hide resolved
type_field: self.return_type.friendly_type_str(),
components: self.return_type.parse_json_abi(),
}],
}
}
}

#[test]
Expand Down Expand Up @@ -623,7 +664,7 @@ impl<'sc> TypedFunctionDeclaration<'sc> {
self_type,
build_config,
dead_code_graph,
dependency_graph
dependency_graph,
),
(
TypedCodeBlock {
Expand Down