Skip to content

Commit

Permalink
Bootstrap the EVM backend. (FuelLabs#3685)
Browse files Browse the repository at this point in the history
This bootstraps the EVM backend via the following:

- [x] Add build target support. This is implemented as a `--build-target
/ --target` CLI flag in Forc.
- [x] Allow for multiple asm builders The existing architecture was
refactored to be trait-based, and the code was split into two
directories, for `fuel` and `evm` backends.
- [x] Add minimal EVM assembly output. 
- [x] Add `evm` target testing support.
- [x] Implement EVM smart contract memory loader
- [x] Implement basic Solidity ABI generation
  • Loading branch information
tritao authored Jan 13, 2023
1 parent be446d1 commit 28899f8
Show file tree
Hide file tree
Showing 40 changed files with 3,589 additions and 2,225 deletions.
451 changes: 447 additions & 4 deletions Cargo.lock

Large diffs are not rendered by default.

108 changes: 79 additions & 29 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use std::{
str::FromStr,
};
use sway_core::{
asm_generation::ProgramABI,
decl_engine::DeclEngine,
fuel_prelude::{
fuel_crypto,
Expand All @@ -38,7 +39,7 @@ use sway_core::{
},
semantic_analysis::namespace,
source_map::SourceMap,
CompileResult, CompiledBytecode, Engines, FinalizedEntry, TypeEngine,
BuildTarget, CompileResult, CompiledBytecode, Engines, FinalizedEntry, TypeEngine,
};
use sway_error::error::CompileError;
use sway_types::Ident;
Expand Down Expand Up @@ -86,7 +87,8 @@ pub struct PinnedId(u64);
/// The result of successfully compiling a package.
#[derive(Debug, Clone)]
pub struct BuiltPackage {
pub json_abi_program: fuels_types::ProgramABI,
pub build_target: BuildTarget,
pub json_abi_program: ProgramABI,
pub storage_slots: Vec<StorageSlot>,
pub bytecode: Vec<u8>,
pub entries: Vec<FinalizedEntry>,
Expand Down Expand Up @@ -313,6 +315,8 @@ pub struct BuildOpts {
pub binary_outfile: Option<String>,
/// If set, outputs source file mapping in JSON format
pub debug_outfile: Option<String>,
/// Build target to use.
pub build_target: BuildTarget,
/// Name of the build profile to use.
/// If it is not specified, forc will use debug build profile.
pub build_profile: Option<String>,
Expand Down Expand Up @@ -371,19 +375,35 @@ impl BuiltPackage {

self.write_bytecode(&bin_path)?;

if !self.json_abi_program.functions.is_empty() {
let json_abi_program_stem = format!("{}-abi", pkg_name);
let json_abi_program_path = output_dir
.join(json_abi_program_stem)
.with_extension("json");
let file = File::create(json_abi_program_path)?;
let res = if minify.json_abi {
serde_json::to_writer(&file, &self.json_abi_program)
} else {
serde_json::to_writer_pretty(&file, &self.json_abi_program)
};
res?
let json_abi_program_stem = format!("{}-abi", pkg_name);
let json_abi_program_path = output_dir
.join(json_abi_program_stem)
.with_extension("json");
match &self.json_abi_program {
ProgramABI::Fuel(json_abi_program) => {
if !json_abi_program.functions.is_empty() {
let file = File::create(json_abi_program_path)?;
let res = if minify.json_abi {
serde_json::to_writer(&file, &json_abi_program)
} else {
serde_json::to_writer_pretty(&file, &json_abi_program)
};
res?
}
}
ProgramABI::Evm(json_abi_program) => {
if !json_abi_program.is_empty() {
let file = File::create(json_abi_program_path)?;
let res = if minify.json_abi {
serde_json::to_writer(&file, &json_abi_program)
} else {
serde_json::to_writer_pretty(&file, &json_abi_program)
};
res?
}
}
}

info!(" Bytecode size is {} bytes.", self.bytecode.len());
// Additional ops required depending on the program type
match self.tree_type {
Expand Down Expand Up @@ -2147,13 +2167,15 @@ fn dep_to_source_patched(
pub fn sway_build_config(
manifest_dir: &Path,
entry_path: &Path,
build_target: BuildTarget,
build_profile: &BuildProfile,
) -> Result<sway_core::BuildConfig> {
// Prepare the build config to pass through to the compiler.
let file_name = find_file_name(manifest_dir, entry_path)?;
let build_config = sway_core::BuildConfig::root_from_file_name_and_manifest_path(
file_name.to_path_buf(),
manifest_dir.to_path_buf(),
build_target,
)
.print_dca_graph(build_profile.print_dca_graph)
.print_finalized_asm(build_profile.print_finalized_asm)
Expand Down Expand Up @@ -2294,12 +2316,17 @@ fn find_core_dep(graph: &Graph, node: NodeIx) -> Option<NodeIx> {
pub fn compile_ast(
engines: Engines<'_>,
manifest: &PackageManifestFile,
build_target: BuildTarget,
build_profile: &BuildProfile,
namespace: namespace::Module,
) -> Result<CompileResult<ty::TyProgram>> {
let source = manifest.entry_string()?;
let sway_build_config =
sway_build_config(manifest.dir(), &manifest.entry_path(), build_profile)?;
let sway_build_config = sway_build_config(
manifest.dir(),
&manifest.entry_path(),
build_target,
build_profile,
)?;
let ast_res = sway_core::compile_to_ast(engines, source, namespace, Some(&sway_build_config));
Ok(ast_res)
}
Expand All @@ -2325,6 +2352,7 @@ pub fn compile_ast(
pub fn compile(
pkg: &Pinned,
manifest: &PackageManifestFile,
build_target: BuildTarget,
build_profile: &BuildProfile,
namespace: namespace::Module,
engines: Engines<'_>,
Expand All @@ -2351,7 +2379,7 @@ pub fn compile(
let entry_path = manifest.entry_path();
let sway_build_config = time_expr!(
"produce `sway_core::BuildConfig`",
sway_build_config(manifest.dir(), &entry_path, build_profile,)?
sway_build_config(manifest.dir(), &entry_path, build_target, build_profile)?
);
let terse_mode = build_profile.terse;
let fail = |warnings, errors| {
Expand All @@ -2362,7 +2390,7 @@ pub fn compile(
// First, compile to an AST. We'll update the namespace and check for JSON ABI output.
let ast_res = time_expr!(
"compile to ast",
compile_ast(engines, manifest, build_profile, namespace)?
compile_ast(engines, manifest, build_target, build_profile, namespace)?
);
let typed_program = match ast_res.value.as_ref() {
None => return fail(&ast_res.warnings, &ast_res.errors),
Expand All @@ -2373,12 +2401,6 @@ pub fn compile(
tracing::info!("{:#?}", typed_program);
}

let mut types = vec![];
let json_abi_program = time_expr!(
"generate JSON ABI program",
typed_program.generate_json_abi_program(engines.te(), &mut types)
);

let storage_slots = typed_program.storage_slots.clone();
let tree_type = typed_program.kind.tree_type();

Expand All @@ -2392,6 +2414,21 @@ pub fn compile(
"compile ast to asm",
sway_core::ast_to_asm(engines, &ast_res, &sway_build_config)
);

let json_abi_program = match build_target {
BuildTarget::Fuel => {
let mut types = vec![];
ProgramABI::Fuel(time_expr!(
"generate JSON ABI program",
typed_program.generate_json_abi_program(engines.te(), &mut types)
))
}
BuildTarget::EVM => match &asm_res.value {
Some(ref v) => v.0.abi.as_ref().unwrap().clone(),
None => todo!(),
},
};

let entries = asm_res
.value
.as_ref()
Expand All @@ -2407,6 +2444,7 @@ pub fn compile(
print_on_success(terse_mode, &pkg.name, &bc_res.warnings, &tree_type);
let bytecode = bytes;
let built_package = BuiltPackage {
build_target,
json_abi_program,
storage_slots,
bytecode,
Expand Down Expand Up @@ -2505,6 +2543,7 @@ pub fn build_with_options(build_options: BuildOpts) -> Result<Built> {
binary_outfile,
debug_outfile,
pkg,
build_target,
..
} = &build_options;

Expand Down Expand Up @@ -2539,7 +2578,7 @@ pub fn build_with_options(build_options: BuildOpts) -> Result<Built> {
};
// Build it!
let mut built_workspace = HashMap::new();
let built_packages = build(&build_plan, &build_profile, &outputs)?;
let built_packages = build(&build_plan, *build_target, &build_profile, &outputs)?;
let output_dir = pkg.output_directory.as_ref().map(PathBuf::from);
for (node_ix, built_package) in built_packages.into_iter() {
let pinned = &graph[node_ix];
Expand Down Expand Up @@ -2613,6 +2652,7 @@ fn validate_contract_deps(graph: &Graph) -> Result<()> {
/// Also returns the resulting `sway_core::SourceMap` which may be useful for debugging purposes.
pub fn build(
plan: &BuildPlan,
target: BuildTarget,
profile: &BuildProfile,
outputs: &HashSet<NodeIx>,
) -> anyhow::Result<Vec<(NodeIx, BuiltPackage)>> {
Expand Down Expand Up @@ -2655,6 +2695,7 @@ pub fn build(
let res = compile(
pkg,
manifest,
target,
profile,
dep_namespace,
engines,
Expand All @@ -2673,7 +2714,9 @@ pub fn build(
lib_namespace_map.insert(node, namespace.into());
}
source_map.insert_dependency(manifest.dir());
standardize_json_abi_types(&mut built_package.json_abi_program);
if let ProgramABI::Fuel(ref mut json_abi_program) = built_package.json_abi_program {
standardize_json_abi_types(json_abi_program);
}
if outputs.contains(&node) {
built_packages.push((node, built_package));
}
Expand Down Expand Up @@ -2833,6 +2876,7 @@ impl Programs {
/// The final item in the returned vector is the project.
pub fn check(
plan: &BuildPlan,
build_target: BuildTarget,
terse_mode: bool,
include_tests: bool,
engines: Engines<'_>,
Expand Down Expand Up @@ -2861,7 +2905,7 @@ pub fn check(
value,
mut warnings,
mut errors,
} = parse(manifest, terse_mode, include_tests, engines)?;
} = parse(manifest, build_target, terse_mode, include_tests, engines)?;

let (lexed, parsed) = match value {
None => {
Expand Down Expand Up @@ -2904,6 +2948,7 @@ pub fn check(
/// Returns a parsed AST from the supplied [PackageManifestFile]
pub fn parse(
manifest: &PackageManifestFile,
build_target: BuildTarget,
terse_mode: bool,
include_tests: bool,
engines: Engines<'_>,
Expand All @@ -2913,8 +2958,13 @@ pub fn parse(
..BuildProfile::debug()
};
let source = manifest.entry_string()?;
let sway_build_config = sway_build_config(manifest.dir(), &manifest.entry_path(), &profile)?
.include_tests(include_tests);
let sway_build_config = sway_build_config(
manifest.dir(),
&manifest.entry_path(),
build_target,
&profile,
)?
.include_tests(include_tests);
Ok(sway_core::parse(source, engines, Some(&sway_build_config)))
}

Expand Down
3 changes: 3 additions & 0 deletions forc-plugins/forc-client/src/ops/deploy/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ pub struct DeployCommand {
/// If url is specified overrides network url in manifest file (if there is one).
#[clap(long, short)]
pub url: Option<String>,
/// Build target for code generation.
//#[clap(long)]
//pub build_target: BuildTarget,
/// Name of the build profile to use.
/// If it is not specified, forc will use debug build profile.
#[clap(long)]
Expand Down
2 changes: 2 additions & 0 deletions forc-plugins/forc-client/src/ops/deploy/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use pkg::BuiltPackage;
use std::path::PathBuf;
use std::time::Duration;
use sway_core::language::parsed::TreeType;
use sway_core::BuildTarget;
use sway_utils::constants::DEFAULT_NODE_URL;
use tracing::info;

Expand Down Expand Up @@ -138,6 +139,7 @@ fn build_opts_from_cmd(cmd: &DeployCommand) -> pkg::BuildOpts {
json_abi: cmd.minify_json_abi,
json_storage_slots: cmd.minify_json_storage_slots,
},
build_target: BuildTarget::default(),
build_profile: cmd.build_profile.clone(),
release: cmd.release,
time_phases: cmd.time_phases,
Expand Down
2 changes: 2 additions & 0 deletions forc-plugins/forc-client/src/ops/run/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use pkg::BuiltPackage;
use std::time::Duration;
use std::{path::PathBuf, str::FromStr};
use sway_core::language::parsed::TreeType;
use sway_core::BuildTarget;
use tokio::time::timeout;
use tracing::info;

Expand Down Expand Up @@ -194,6 +195,7 @@ fn build_opts_from_cmd(cmd: &RunCommand) -> pkg::BuildOpts {
json_abi: cmd.minify_json_abi,
json_storage_slots: cmd.minify_json_storage_slots,
},
build_target: BuildTarget::default(),
build_profile: cmd.build_profile.clone(),
release: cmd.release,
time_phases: cmd.time_phases,
Expand Down
16 changes: 11 additions & 5 deletions forc-plugins/forc-doc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::{
process::Command as Process,
{fs, path::PathBuf},
};
use sway_core::{decl_engine::DeclEngine, Engines, TypeEngine};
use sway_core::{decl_engine::DeclEngine, BuildTarget, Engines, TypeEngine};

/// Main method for `forc doc`.
pub fn main() -> Result<()> {
Expand Down Expand Up @@ -61,10 +61,16 @@ pub fn main() -> Result<()> {
let decl_engine = DeclEngine::default();
let engines = Engines::new(&type_engine, &decl_engine);
let tests_enabled = true;
let typed_program = match pkg::check(&plan, silent, tests_enabled, engines)?
.pop()
.and_then(|compilation| compilation.value)
.and_then(|programs| programs.typed)
let typed_program = match pkg::check(
&plan,
BuildTarget::default(),
silent,
tests_enabled,
engines,
)?
.pop()
.and_then(|compilation| compilation.value)
.and_then(|programs| programs.typed)
{
Some(typed_program) => typed_program,
_ => bail!("CompileResult returned None"),
Expand Down
3 changes: 2 additions & 1 deletion forc-plugins/forc-fmt/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tracing::{error, info};

use forc_tracing::{init_tracing_subscriber, println_green, println_red};
use forc_util::find_manifest_dir;
use sway_core::BuildConfig;
use sway_core::{BuildConfig, BuildTarget};
use sway_utils::{constants, get_sway_files};
use swayfmt::Formatter;

Expand Down Expand Up @@ -69,6 +69,7 @@ fn format_pkg_at_dir(app: App, dir: &Path, formatter: &mut Formatter) -> Result<
let build_config = BuildConfig::root_from_file_name_and_manifest_path(
file.clone(),
manifest_path.clone(),
BuildTarget::default(),
);
match Formatter::format(formatter, file_content.clone(), Some(&build_config)) {
Ok(formatted_content) => {
Expand Down
5 changes: 4 additions & 1 deletion forc-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use fuel_tx as tx;
use fuel_vm::{self as vm, prelude::Opcode};
use pkg::{Built, BuiltPackage};
use rand::{distributions::Standard, prelude::Distribution, Rng, SeedableRng};
use sway_core::{language::ty::TyFunctionDeclaration, transform::AttributeKind};
use sway_core::{language::ty::TyFunctionDeclaration, transform::AttributeKind, BuildTarget};
use sway_types::{Span, Spanned};
use tx::{AssetId, TxPointer, UtxoId};
use vm::prelude::SecretKey;
Expand Down Expand Up @@ -91,6 +91,8 @@ pub struct Opts {
pub binary_outfile: Option<String>,
/// If set, outputs source file mapping in JSON format
pub debug_outfile: Option<String>,
/// Build target to use.
pub build_target: BuildTarget,
/// Name of the build profile to use.
/// If it is not specified, forc will use debug build profile.
pub build_profile: Option<String>,
Expand Down Expand Up @@ -266,6 +268,7 @@ impl Opts {
minify: self.minify,
binary_outfile: self.binary_outfile,
debug_outfile: self.debug_outfile,
build_target: self.build_target,
build_profile: self.build_profile,
release: self.release,
time_phases: self.time_phases,
Expand Down
Loading

0 comments on commit 28899f8

Please sign in to comment.