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

Implement initial support for source mapping #656

Merged
merged 2 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 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 forc/src/cli/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub struct Command {
/// If set, outputs a binary file representing the script bytes.
#[structopt(short = "o")]
pub binary_outfile: Option<String>,
/// If set, outputs source file mapping in JSON format
#[structopt(short = "g", long)]
pub debug_outfile: Option<String>,
/// Offline mode, prevents Forc from using the network when managing dependencies.
/// Meaning it will only try to use previously downloaded dependencies.
#[structopt(long = "offline")]
Expand Down
3 changes: 3 additions & 0 deletions forc/src/cli/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub struct Command {
/// If set, outputs a binary file representing the script bytes.
#[structopt(short = "o")]
pub binary_outfile: Option<String>,
/// If set, outputs source file mapping in JSON format
#[structopt(short = "g", long)]
pub debug_outfile: Option<String>,
/// Offline mode, prevents Forc from using the network when managing dependencies.
/// Meaning it will only try to use previously downloaded dependencies.
#[structopt(long = "offline")]
Expand Down
4 changes: 4 additions & 0 deletions forc/src/cli/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ pub struct Command {
#[structopt(short = "o")]
pub binary_outfile: Option<String>,

/// If set, outputs source file mapping in JSON format
#[structopt(short = "g", long)]
pub debug_outfile: Option<String>,

/// Silent mode. Don't output any warnings or errors to the command line.
#[structopt(long = "silent", short = "s")]
pub silent_mode: bool,
Expand Down
29 changes: 25 additions & 4 deletions forc/src/ops/forc_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ use crate::{
get_main_file, print_on_failure, print_on_success, print_on_success_library, read_manifest,
},
};
use std::fs::File;
use std::fs::{self, File};
use std::io::Write;
use std::sync::Arc;
use sway_core::{FinalizedAsm, TreeType};
use sway_utils::{constants, find_manifest_dir};

use sway_core::{
create_module, BuildConfig, BytecodeCompilationResult, CompilationResult, NamespaceRef,
NamespaceWrapper,
create_module, source_map::SourceMap, BuildConfig, BytecodeCompilationResult,
CompilationResult, NamespaceRef, NamespaceWrapper,
};

use anyhow::Result;
Expand All @@ -33,6 +33,7 @@ pub fn build(command: BuildCommand) -> Result<Vec<u8>, String> {
let BuildCommand {
binary_outfile,
use_ir,
debug_outfile,
print_finalized_asm,
print_intermediate_asm,
print_ir,
Expand Down Expand Up @@ -94,19 +95,31 @@ pub fn build(command: BuildCommand) -> Result<Vec<u8>, String> {
// now, compile this program with all of its dependencies
let main_file = get_main_file(&manifest, &manifest_dir)?;

let mut source_map = SourceMap::new();

let main = compile(
main_file,
&manifest.project.name,
namespace,
build_config,
&mut dependency_graph,
&mut source_map,
silent_mode,
)?;

if let Some(outfile) = binary_outfile {
let mut file = File::create(outfile).map_err(|e| e.to_string())?;
file.write_all(main.as_slice()).map_err(|e| e.to_string())?;
}

if let Some(outfile) = debug_outfile {
fs::write(
outfile,
&serde_json::to_vec(&source_map).expect("JSON seralizatio failed"),
)
.map_err(|e| e.to_string())?;
}

println!(" Bytecode size is {} bytes.", main.len());

Ok(main)
Expand Down Expand Up @@ -269,9 +282,17 @@ fn compile(
namespace: NamespaceRef,
build_config: BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
source_map: &mut SourceMap,
silent_mode: bool,
) -> Result<Vec<u8>, String> {
let res = sway_core::compile_to_bytecode(source, namespace, build_config, dependency_graph);
let res = sway_core::compile_to_bytecode(
source,
namespace,
build_config,
dependency_graph,
source_map,
);

match res {
BytecodeCompilationResult::Success { bytes, warnings } => {
print_on_success(silent_mode, proj_name, warnings, TreeType::Script {});
Expand Down
2 changes: 2 additions & 0 deletions forc/src/ops/forc_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub async fn deploy(command: DeployCommand) -> Result<fuel_tx::ContractId, CliEr
print_intermediate_asm,
print_ir,
binary_outfile,
debug_outfile,
offline_mode,
silent_mode,
} = command;
Expand All @@ -49,6 +50,7 @@ pub async fn deploy(command: DeployCommand) -> Result<fuel_tx::ContractId, CliEr
print_ir,
binary_outfile,
offline_mode,
debug_outfile,
silent_mode,
};

Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn format(command: FormatCommand) -> Result<(), FormatError> {
print_intermediate_asm: false,
print_ir: false,
binary_outfile: None,
debug_outfile: None,
offline_mode: false,
silent_mode: false,
};
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub async fn run(command: RunCommand) -> Result<(), CliError> {
print_intermediate_asm: command.print_intermediate_asm,
print_ir: command.print_ir,
binary_outfile: command.binary_outfile,
debug_outfile: command.debug_outfile,
offline_mode: false,
silent_mode: command.silent_mode,
};
Expand Down
1 change: 1 addition & 0 deletions sway-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pest = { version = "3.0.4", package = "fuel-pest" }
pest_derive = { version = "3.0.4", package = "fuel-pest_derive" }
petgraph = "0.5"
prettydiff = "0.5"
serde = { version = "1.0", features = ["derive"] }
sha2 = "0.9"
smallvec = "1.7"
structopt = { version = "0.3", default-features = false, optional = true }
Expand Down
15 changes: 11 additions & 4 deletions sway-core/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::{DataSection, InstructionSet};
use crate::asm_lang::allocated_ops::AllocatedOpcode;
use crate::error::*;
use crate::source_map::SourceMap;

use sway_types::span::Span;

use either::Either;
Expand All @@ -26,30 +28,31 @@ pub enum FinalizedAsm {
Library,
}
impl FinalizedAsm {
pub(crate) fn to_bytecode_mut(&mut self) -> CompileResult<Vec<u8>> {
pub(crate) fn to_bytecode_mut(&mut self, source_map: &mut SourceMap) -> CompileResult<Vec<u8>> {
use FinalizedAsm::*;
match self {
ContractAbi {
program_section,
ref mut data_section,
} => to_bytecode_mut(program_section, data_section),
} => to_bytecode_mut(program_section, data_section, source_map),
// libraries are not compiled to asm
Library => ok(vec![], vec![], vec![]),
ScriptMain {
program_section,
ref mut data_section,
} => to_bytecode_mut(program_section, data_section),
} => to_bytecode_mut(program_section, data_section, source_map),
PredicateMain {
program_section,
ref mut data_section,
} => to_bytecode_mut(program_section, data_section),
} => to_bytecode_mut(program_section, data_section, source_map),
}
}
}

fn to_bytecode_mut(
program_section: &InstructionSet,
data_section: &mut DataSection,
source_map: &mut SourceMap,
) -> CompileResult<Vec<u8>> {
let mut errors = vec![];
if program_section.ops.len() & 1 != 0 {
Expand Down Expand Up @@ -92,6 +95,7 @@ fn to_bytecode_mut(

let mut half_word_ix = 0;
for op in program_section.ops.iter() {
let span = op.owning_span.clone();
let op = op.to_fuel_asm(offset_to_data_section_in_bytes, data_section);
match op {
Either::Right(data) => {
Expand All @@ -105,6 +109,9 @@ fn to_bytecode_mut(
buf.resize(buf.len() + ((ops.len() - 1) * 4), 0);
}
for mut op in ops {
if let Some(span) = &span {
source_map.insert(half_word_ix, span);
}
op.read_exact(&mut buf[half_word_ix * 4..])
.expect("Failed to write to in-memory buffer.");
half_word_ix += 1;
Expand Down
5 changes: 4 additions & 1 deletion sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ mod optimize;
pub mod parse_tree;
mod parser;
pub mod semantic_analysis;
pub mod source_map;
mod style;
pub mod type_engine;

pub use crate::parser::{Rule, SwayParser};
use crate::{
asm_generation::{checks, compile_ast_to_asm},
error::*,
source_map::SourceMap,
};
pub use asm_generation::{AbstractInstructionSet, FinalizedAsm, SwayAsmSet};
pub use build_config::BuildConfig;
Expand Down Expand Up @@ -506,13 +508,14 @@ pub fn compile_to_bytecode(
initial_namespace: crate::semantic_analysis::NamespaceRef,
build_config: BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
source_map: &mut SourceMap,
) -> BytecodeCompilationResult {
match compile_to_asm(input, initial_namespace, build_config, dependency_graph) {
CompilationResult::Success {
mut asm,
mut warnings,
} => {
let mut asm_res = asm.to_bytecode_mut();
let mut asm_res = asm.to_bytecode_mut(source_map);
warnings.append(&mut asm_res.warnings);
if asm_res.value.is_none() || !asm_res.errors.is_empty() {
BytecodeCompilationResult::Failure {
Expand Down
57 changes: 57 additions & 0 deletions sway-core/src/source_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::collections::HashMap;
use std::path::PathBuf;

use serde::{Deserialize, Serialize};

use sway_types::span::Span;

/// Index of an interned path string
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PathIndex(usize);

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SourceMap {
paths: Vec<PathBuf>,
map: HashMap<usize, SourceMapSpan>,
}
impl SourceMap {
pub fn new() -> Self {
Self::default()
}

pub fn insert(&mut self, pc: usize, span: &Span) {
if let Some(path) = span.path.as_ref() {
let path_index = self
.paths
.iter()
.position(|p| *p == **path)
.unwrap_or_else(|| {
self.paths.push((**path).to_owned());
self.paths.len() - 1
});
self.map.insert(
pc,
SourceMapSpan {
path: PathIndex(path_index),
range: LocationRange {
start: span.start(),
end: span.end(),
},
},
);
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceMapSpan {
pub path: PathIndex,
pub range: LocationRange,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocationRange {
pub start: usize,
pub end: usize,
}
3 changes: 3 additions & 0 deletions test/src/e2e_vm_tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(crate) fn deploy_contract(file_name: &str) -> ContractId {
print_intermediate_asm: false,
print_ir: false,
binary_outfile: None,
debug_outfile: None,
offline_mode: false,
silent_mode: true,
}))
Expand Down Expand Up @@ -54,6 +55,7 @@ pub(crate) fn runs_on_node(file_name: &str, contract_ids: &[fuel_tx::ContractId]
kill_node: false,
use_ir: false,
binary_outfile: None,
debug_outfile: None,
print_finalized_asm: false,
print_intermediate_asm: false,
print_ir: false,
Expand Down Expand Up @@ -121,6 +123,7 @@ pub(crate) fn compile_to_bytes(file_name: &str) -> Result<Vec<u8>, String> {
print_intermediate_asm: false,
print_ir: false,
binary_outfile: None,
debug_outfile: None,
offline_mode: false,
silent_mode: true,
})
Expand Down