mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-25 16:27:57 +00:00
experimental: support for debug info (#118)
Signed-off-by: wpt967 <matt.aw@parity.io> Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
@@ -212,9 +212,10 @@ where
|
||||
) -> anyhow::Result<()> {
|
||||
let full_path = self.full_path().to_owned();
|
||||
|
||||
if let Some(debug_config) = context.debug_config() {
|
||||
debug_config.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
|
||||
}
|
||||
context
|
||||
.debug_config()
|
||||
.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
|
||||
|
||||
let deploy_code_blocks = EtherealIR::get_blocks(
|
||||
context.evmla().version.to_owned(),
|
||||
revive_llvm_context::PolkaVMCodeType::Deploy,
|
||||
@@ -228,9 +229,11 @@ where
|
||||
.ok_or_else(|| anyhow::anyhow!("Runtime code data not found"))?
|
||||
.remove("0")
|
||||
.expect("Always exists");
|
||||
if let Some(debug_config) = context.debug_config() {
|
||||
debug_config.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
|
||||
}
|
||||
|
||||
context
|
||||
.debug_config()
|
||||
.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
|
||||
|
||||
let runtime_code_instructions = match data {
|
||||
Data::Assembly(assembly) => assembly
|
||||
.code
|
||||
@@ -253,9 +256,11 @@ where
|
||||
blocks.extend(runtime_code_blocks);
|
||||
let mut ethereal_ir =
|
||||
EtherealIR::new(context.evmla().version.to_owned(), extra_metadata, blocks)?;
|
||||
if let Some(debug_config) = context.debug_config() {
|
||||
debug_config.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
|
||||
}
|
||||
|
||||
context
|
||||
.debug_config()
|
||||
.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
|
||||
|
||||
ethereal_ir.declare(context)?;
|
||||
ethereal_ir.into_llvm(context)?;
|
||||
|
||||
|
||||
@@ -1175,7 +1175,7 @@ where
|
||||
}
|
||||
|
||||
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
||||
context.set_current_function(self.name.as_str())?;
|
||||
context.set_current_function(self.name.as_str(), None)?;
|
||||
|
||||
for (key, blocks) in self.blocks.iter() {
|
||||
for (index, block) in blocks.iter().enumerate() {
|
||||
@@ -1297,6 +1297,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
context.pop_debug_scope();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ pub fn yul(
|
||||
solc: &mut SolcCompiler,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
include_metadata_hash: bool,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<Build> {
|
||||
let path = match input_files.len() {
|
||||
1 => input_files.first().expect("Always exists"),
|
||||
@@ -85,7 +85,7 @@ pub fn llvm_ir(
|
||||
input_files: &[PathBuf],
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
include_metadata_hash: bool,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<Build> {
|
||||
let path = match input_files.len() {
|
||||
1 => input_files.first().expect("Always exists"),
|
||||
@@ -119,7 +119,7 @@ pub fn standard_output(
|
||||
allow_paths: Option<String>,
|
||||
remappings: Option<BTreeSet<String>>,
|
||||
suppressed_warnings: Option<Vec<Warning>>,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<Build> {
|
||||
let solc_version = solc.version()?;
|
||||
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
|
||||
@@ -178,7 +178,7 @@ pub fn standard_output(
|
||||
libraries,
|
||||
solc_pipeline,
|
||||
&solc_version,
|
||||
debug_config.as_ref(),
|
||||
&debug_config,
|
||||
)?;
|
||||
|
||||
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
|
||||
@@ -195,7 +195,7 @@ pub fn standard_json(
|
||||
base_path: Option<String>,
|
||||
include_paths: Vec<String>,
|
||||
allow_paths: Option<String>,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<()> {
|
||||
let solc_version = solc.version()?;
|
||||
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
|
||||
@@ -240,7 +240,7 @@ pub fn standard_json(
|
||||
libraries,
|
||||
solc_pipeline,
|
||||
&solc_version,
|
||||
debug_config.as_ref(),
|
||||
&debug_config,
|
||||
)?;
|
||||
|
||||
if detect_missing_libraries {
|
||||
@@ -271,7 +271,7 @@ pub fn combined_json(
|
||||
allow_paths: Option<String>,
|
||||
remappings: Option<BTreeSet<String>>,
|
||||
suppressed_warnings: Option<Vec<Warning>>,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
output_directory: Option<PathBuf>,
|
||||
overwrite: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct Input {
|
||||
/// The optimizer settings.
|
||||
pub optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
/// The debug output config.
|
||||
pub debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
pub debug_config: revive_llvm_context::DebugConfig,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
@@ -29,7 +29,7 @@ impl Input {
|
||||
project: Project,
|
||||
include_metadata_hash: bool,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
contract,
|
||||
|
||||
@@ -85,17 +85,16 @@ pub fn call(input: Input) -> anyhow::Result<Output> {
|
||||
})?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(dbg_config) = &input.debug_config {
|
||||
dbg_config
|
||||
.dump_stage_output(&input.contract.path, Some("stage"), &input_json)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{:?} failed to log the recursive process output: {:?}",
|
||||
executable,
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
input
|
||||
.debug_config
|
||||
.dump_stage_output(&input.contract.path, Some("stage"), &input_json)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{:?} failed to log the recursive process output: {:?}",
|
||||
executable,
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
|
||||
process
|
||||
.stdin
|
||||
|
||||
@@ -79,7 +79,7 @@ impl Contract {
|
||||
project: Project,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
include_metadata_hash: bool,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<ContractBuild> {
|
||||
let llvm = inkwell::context::Context::create();
|
||||
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
|
||||
@@ -104,6 +104,7 @@ impl Contract {
|
||||
|
||||
let module = match self.ir {
|
||||
IR::LLVMIR(ref llvm_ir) => {
|
||||
// Create the output module
|
||||
let memory_buffer =
|
||||
inkwell::memory_buffer::MemoryBuffer::create_from_memory_range_copy(
|
||||
llvm_ir.source.as_bytes(),
|
||||
@@ -114,6 +115,7 @@ impl Contract {
|
||||
}
|
||||
_ => llvm.create_module(self.path.as_str()),
|
||||
};
|
||||
|
||||
let mut context = revive_llvm_context::PolkaVMContext::new(
|
||||
&llvm,
|
||||
module,
|
||||
@@ -151,6 +153,10 @@ impl Contract {
|
||||
)
|
||||
})?;
|
||||
|
||||
if let Some(debug_info) = context.debug_info() {
|
||||
debug_info.finalize_module()
|
||||
}
|
||||
|
||||
let build = context.build(self.path.as_str(), metadata_hash)?;
|
||||
|
||||
Ok(ContractBuild::new(
|
||||
|
||||
@@ -63,7 +63,7 @@ impl Project {
|
||||
self,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
include_metadata_hash: bool,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<Build> {
|
||||
let project = self.clone();
|
||||
let results: BTreeMap<String, anyhow::Result<ContractBuild>> = self
|
||||
@@ -238,7 +238,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
|
||||
identifier: &str,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
include_metadata_hash: bool,
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
debug_config: revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<String> {
|
||||
let contract_path = project.resolve_path(identifier)?;
|
||||
let contract = project
|
||||
|
||||
@@ -138,6 +138,11 @@ pub struct Arguments {
|
||||
#[structopt(long = "suppress-warnings")]
|
||||
pub suppress_warnings: Option<Vec<String>>,
|
||||
|
||||
/// Generate source based debug information in the output code file. This only has an effect
|
||||
/// with the LLVM-IR code generator and is ignored otherwise.
|
||||
#[structopt(short = 'g')]
|
||||
pub emit_source_debug_info: bool,
|
||||
|
||||
/// Dump all IRs to files in the specified directory.
|
||||
/// Only for testing and debugging.
|
||||
#[structopt(long = "debug-output-dir")]
|
||||
|
||||
@@ -66,11 +66,12 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
let debug_config = match arguments.debug_output_directory {
|
||||
Some(ref debug_output_directory) => {
|
||||
std::fs::create_dir_all(debug_output_directory.as_path())?;
|
||||
Some(revive_llvm_context::DebugConfig::new(
|
||||
debug_output_directory.to_owned(),
|
||||
))
|
||||
revive_llvm_context::DebugConfig::new(
|
||||
Some(debug_output_directory.to_owned()),
|
||||
arguments.emit_source_debug_info,
|
||||
)
|
||||
}
|
||||
None => None,
|
||||
None => revive_llvm_context::DebugConfig::new(None, arguments.emit_source_debug_info),
|
||||
};
|
||||
|
||||
let (input_files, remappings) = arguments.split_input_files_and_remappings()?;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl Output {
|
||||
libraries: BTreeMap<String, BTreeMap<String, String>>,
|
||||
pipeline: SolcPipeline,
|
||||
solc_version: &SolcVersion,
|
||||
debug_config: Option<&revive_llvm_context::DebugConfig>,
|
||||
debug_config: &revive_llvm_context::DebugConfig,
|
||||
) -> anyhow::Result<Project> {
|
||||
if let SolcPipeline::EVMLA = pipeline {
|
||||
self.preprocess_dependencies()?;
|
||||
@@ -90,9 +90,7 @@ impl Output {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(debug_config) = debug_config {
|
||||
debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?;
|
||||
}
|
||||
debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?;
|
||||
|
||||
let mut lexer = Lexer::new(ir_optimized.to_owned());
|
||||
let object = Object::parse(&mut lexer, None).map_err(|error| {
|
||||
|
||||
@@ -23,6 +23,9 @@ static EVM_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> = Lazy::new(Def
|
||||
static EVM_RUNTIME_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> =
|
||||
Lazy::new(Default::default);
|
||||
|
||||
const DEBUG_CONFIG: revive_llvm_context::DebugConfig =
|
||||
revive_llvm_context::DebugConfig::new(None, true);
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct CachedBlob {
|
||||
contract_name: String,
|
||||
@@ -102,9 +105,10 @@ pub fn build_solidity_with_options(
|
||||
|
||||
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?;
|
||||
|
||||
let project = output.try_to_project(sources, libraries, pipeline, &solc_version, None)?;
|
||||
let project =
|
||||
output.try_to_project(sources, libraries, pipeline, &solc_version, &DEBUG_CONFIG)?;
|
||||
|
||||
let build: crate::Build = project.compile(optimizer_settings, false, None)?;
|
||||
let build: crate::Build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
|
||||
build.write_to_standard_json(&mut output, &solc_version)?;
|
||||
|
||||
Ok(output)
|
||||
@@ -194,7 +198,8 @@ pub fn build_solidity_and_detect_missing_libraries(
|
||||
|
||||
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?;
|
||||
|
||||
let project = output.try_to_project(sources, libraries, pipeline, &solc_version, None)?;
|
||||
let project =
|
||||
output.try_to_project(sources, libraries, pipeline, &solc_version, &DEBUG_CONFIG)?;
|
||||
|
||||
let missing_libraries = project.get_missing_libraries();
|
||||
missing_libraries.write_to_standard_json(&mut output, &solc.version()?)?;
|
||||
@@ -212,7 +217,7 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
|
||||
|
||||
let project =
|
||||
Project::try_from_yul_string(PathBuf::from("test.yul").as_path(), source_code, None)?;
|
||||
let _build = project.compile(optimizer_settings, false, None)?;
|
||||
let _build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ import * as path from 'path';
|
||||
const outputDir = 'artifacts';
|
||||
const binExtension = ':C.pvm';
|
||||
const asmExtension = ':C.pvmasm';
|
||||
const llvmExtension = '.ll'
|
||||
const contractSolFilename = 'contract.sol';
|
||||
const contractYulFilename = 'contract.yul';
|
||||
const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized';
|
||||
const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized';
|
||||
const pathToOutputDir = path.join(__dirname, '..', outputDir);
|
||||
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts');
|
||||
const pathToBasicYulContract = path.join(pathToContracts, 'yul', contractYulFilename);
|
||||
@@ -16,8 +19,11 @@ export const paths = {
|
||||
outputDir: outputDir,
|
||||
binExtension: binExtension,
|
||||
asmExtension: asmExtension,
|
||||
llvmExtension: llvmExtension,
|
||||
contractSolFilename: contractSolFilename,
|
||||
contractYulFilename: contractYulFilename,
|
||||
contractOptimizedLLVMFilename: contractOptimizedLLVMFilename,
|
||||
contractUnoptimizedLLVMFilename: contractUnoptimizedLLVMFilename,
|
||||
pathToOutputDir: pathToOutputDir,
|
||||
pathToContracts: pathToContracts,
|
||||
pathToBasicSolContract: pathToBasicSolContract,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {executeCommand, isFolderExist, isFileExist, isFileEmpty} from "../src/helper";
|
||||
import { executeCommand, isFolderExist, isFileExist, isFileEmpty } from "../src/helper";
|
||||
import { paths } from '../src/entities';
|
||||
|
||||
|
||||
@@ -76,3 +76,69 @@ describe("Default run a command from the help", () => {
|
||||
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Run resolc with source debug information", () => {
|
||||
const commands = [
|
||||
`resolc -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`,
|
||||
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`
|
||||
]; // potential issue on resolc with full path on Windows cmd`;
|
||||
|
||||
for (var idx in commands) {
|
||||
const command = commands[idx];
|
||||
const result = executeCommand(command);
|
||||
|
||||
it("Compiler run successful", () => {
|
||||
expect(result.output).toMatch(/(Compiler run successful.)/i);
|
||||
});
|
||||
it("Exit code = 0", () => {
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
it("Output dir is created", () => {
|
||||
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
|
||||
});
|
||||
it("Output files are created", () => { // a bug on windows
|
||||
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
|
||||
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
|
||||
});
|
||||
it("the output files are not empty", () => {
|
||||
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
|
||||
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
|
||||
});
|
||||
it("No 'Error'/'Fail' in the output", () => {
|
||||
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("Run resolc with source debug information, check LLVM debug-info", () => {
|
||||
const commands = [
|
||||
`resolc -g ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`,
|
||||
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`
|
||||
]; // potential issue on resolc with full path on Windows cmd`;
|
||||
|
||||
for (var idx in commands) {
|
||||
const command = commands[idx];
|
||||
const result = executeCommand(command);
|
||||
|
||||
it("Compiler run successful", () => {
|
||||
expect(result.output).toMatch(/(Compiler run successful.)/i);
|
||||
});
|
||||
it("Exit code = 0", () => {
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
it("Output dir is created", () => {
|
||||
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
|
||||
});
|
||||
it("Output files are created", () => { // a bug on windows
|
||||
expect(isFileExist(paths.pathToOutputDir, paths.contractOptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
|
||||
expect(isFileExist(paths.pathToOutputDir, paths.contractUnoptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
|
||||
});
|
||||
it("the output files are not empty", () => {
|
||||
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
|
||||
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
|
||||
});
|
||||
it("No 'Error'/'Fail' in the output", () => {
|
||||
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct Lexer {
|
||||
/// The input source code.
|
||||
input: String,
|
||||
/// The number of characters processed so far.
|
||||
offset: usize,
|
||||
offset: u32,
|
||||
/// The current location.
|
||||
location: Location,
|
||||
/// The peeked lexeme, waiting to be fetched.
|
||||
@@ -48,8 +48,17 @@ impl Lexer {
|
||||
return Ok(peeked);
|
||||
}
|
||||
|
||||
while self.offset < self.input.len() {
|
||||
let input = &self.input[self.offset..];
|
||||
while self.offset
|
||||
< self
|
||||
.input
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidLexeme {
|
||||
location: self.location,
|
||||
sequence: Default::default(),
|
||||
})?
|
||||
{
|
||||
let input = &self.input[(self.offset as usize)..];
|
||||
|
||||
if input.starts_with(|character| char::is_ascii_whitespace(&character)) {
|
||||
if input.starts_with('\n') {
|
||||
@@ -101,12 +110,13 @@ impl Lexer {
|
||||
return Ok(token);
|
||||
}
|
||||
|
||||
let end = self.input[self.offset..]
|
||||
let end = self.input[(self.offset as usize)..]
|
||||
.find(char::is_whitespace)
|
||||
.unwrap_or(self.input.len());
|
||||
return Err(Error::InvalidLexeme {
|
||||
location: self.location,
|
||||
sequence: self.input[self.offset..self.offset + end].to_owned(),
|
||||
sequence: self.input[(self.offset as usize)..(self.offset as usize) + end]
|
||||
.to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,20 @@ impl Comment {
|
||||
let end_position = input.find(Self::END).unwrap_or(input.len());
|
||||
let input = &input[..end_position];
|
||||
|
||||
let length = end_position + Self::END.len();
|
||||
let lines = input.matches('\n').count();
|
||||
let length = (end_position + Self::END.len())
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
let lines = input
|
||||
.matches('\n')
|
||||
.count()
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
let columns = match input.rfind('\n') {
|
||||
Some(new_line) => end_position - (new_line + 1),
|
||||
None => end_position,
|
||||
};
|
||||
}
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
|
||||
Token::new(Location::new(lines, columns), Lexeme::Comment, length)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ impl Comment {
|
||||
/// Returns the comment's length, including the trimmed whitespace around it.
|
||||
pub fn parse(input: &str) -> Token {
|
||||
let end_position = input.find(Self::END).unwrap_or(input.len());
|
||||
let length = end_position + Self::END.len();
|
||||
let length = (end_position + Self::END.len())
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
|
||||
Token::new(Location::new(1, 1), Lexeme::Comment, length)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ impl Identifier {
|
||||
let end = input.find(Self::cannot_continue).unwrap_or(input.len());
|
||||
|
||||
let inner = input[..end].to_string();
|
||||
let length = inner.len();
|
||||
let length = inner
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
|
||||
if let Some(token) = Keyword::parse(inner.as_str()) {
|
||||
return Some(token);
|
||||
|
||||
@@ -58,6 +58,9 @@ impl Keyword {
|
||||
if length != input.len() {
|
||||
return None;
|
||||
}
|
||||
let length = length
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
|
||||
Some(Token::new(Location::new(0, length), lexeme, length))
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ impl Integer {
|
||||
return None;
|
||||
};
|
||||
|
||||
let length = length
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
|
||||
let token = Token::new(
|
||||
Location::new(0, length),
|
||||
Lexeme::Literal(Literal::Integer(value)),
|
||||
|
||||
@@ -69,6 +69,9 @@ impl String {
|
||||
.to_owned();
|
||||
|
||||
let literal = Self::new(string, is_hex_string);
|
||||
let length = length
|
||||
.try_into()
|
||||
.expect("the YUL should be of reasonable size");
|
||||
|
||||
Some(Token::new(
|
||||
Location::new(0, length),
|
||||
|
||||
@@ -7,9 +7,9 @@ use serde::Serialize;
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq)]
|
||||
pub struct Location {
|
||||
/// The line number, starting from 1.
|
||||
pub line: usize,
|
||||
pub line: u32,
|
||||
/// The column number, starting from 1.
|
||||
pub column: usize,
|
||||
pub column: u32,
|
||||
}
|
||||
|
||||
impl Default for Location {
|
||||
@@ -20,13 +20,13 @@ impl Default for Location {
|
||||
|
||||
impl Location {
|
||||
/// Creates a default location.
|
||||
pub fn new(line: usize, column: usize) -> Self {
|
||||
pub fn new(line: u32, column: u32) -> Self {
|
||||
Self { line, column }
|
||||
}
|
||||
|
||||
/// Mutates the location by shifting the original one down by `lines` and
|
||||
/// setting the column to `column`.
|
||||
pub fn shift_down(&mut self, lines: usize, column: usize) {
|
||||
pub fn shift_down(&mut self, lines: u32, column: u32) {
|
||||
if lines == 0 {
|
||||
self.shift_right(column);
|
||||
return;
|
||||
@@ -37,7 +37,7 @@ impl Location {
|
||||
}
|
||||
|
||||
/// Mutates the location by shifting the original one rightward by `columns`.
|
||||
pub fn shift_right(&mut self, columns: usize) {
|
||||
pub fn shift_right(&mut self, columns: u32) {
|
||||
self.column += columns;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ pub struct Token {
|
||||
/// The lexeme.
|
||||
pub lexeme: Lexeme,
|
||||
/// The token length, including whitespaces.
|
||||
pub length: usize,
|
||||
pub length: u32,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// A shortcut constructor.
|
||||
pub fn new(location: Location, lexeme: Lexeme, length: usize) -> Self {
|
||||
pub fn new(location: Location, lexeme: Lexeme, length: u32) -> Self {
|
||||
Self {
|
||||
location,
|
||||
lexeme,
|
||||
|
||||
@@ -57,4 +57,7 @@ pub enum Error {
|
||||
/// The list of invalid attributes.
|
||||
values: BTreeSet<String>,
|
||||
},
|
||||
/// Invalid code length.
|
||||
#[error("The line or column length exceed the maximum of u32::MAX")]
|
||||
InvalidLength,
|
||||
}
|
||||
|
||||
@@ -47,7 +47,11 @@ impl Assignment {
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let length = identifier.inner.len();
|
||||
let length = identifier
|
||||
.inner
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::Parser(ParserError::InvalidLength))?;
|
||||
|
||||
match lexer.peek()? {
|
||||
Token {
|
||||
@@ -115,6 +119,8 @@ where
|
||||
mut self,
|
||||
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
|
||||
let value = match self.initializer.into_llvm(context)? {
|
||||
Some(value) => value,
|
||||
None => return Ok(()),
|
||||
@@ -142,6 +148,8 @@ where
|
||||
context.build_store(tuple_pointer, value.to_llvm())?;
|
||||
|
||||
for (index, binding) in self.bindings.into_iter().enumerate() {
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
|
||||
let field_pointer = context.build_gep(
|
||||
tuple_pointer,
|
||||
&[
|
||||
|
||||
@@ -5,6 +5,8 @@ use std::collections::HashSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use inkwell::debug_info::AsDIScope;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
@@ -153,9 +155,26 @@ where
|
||||
function.into_llvm(context)?;
|
||||
}
|
||||
|
||||
context.set_current_function(current_function.as_str())?;
|
||||
context.set_current_function(current_function.as_str(), Some(self.location.line))?;
|
||||
|
||||
if let Some(debug_info) = context.debug_info() {
|
||||
let di_builder = debug_info.builder();
|
||||
let di_scope = debug_info.top_scope().expect("expected a debug-info scope");
|
||||
let di_block_scope = di_builder
|
||||
.create_lexical_block(
|
||||
di_scope,
|
||||
debug_info.compilation_unit().get_file(),
|
||||
self.location.line,
|
||||
0,
|
||||
)
|
||||
.as_debug_info_scope();
|
||||
context.push_debug_scope(di_block_scope);
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
}
|
||||
|
||||
context.set_basic_block(current_block);
|
||||
for statement in local_statements.into_iter() {
|
||||
context.set_debug_location(statement.location().line, 0, None)?;
|
||||
if context.basic_block().get_terminator().is_some() {
|
||||
break;
|
||||
}
|
||||
@@ -194,6 +213,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
context.pop_debug_scope();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,11 @@ impl Expression {
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let length = identifier.inner.len();
|
||||
let length = identifier
|
||||
.inner
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::Parser(ParserError::InvalidLength))?;
|
||||
|
||||
match lexer.peek()? {
|
||||
Token {
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -229,14 +230,15 @@ where
|
||||
mut self,
|
||||
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
context.set_current_function(self.identifier.as_str())?;
|
||||
let r#return = context.current_function().borrow().r#return();
|
||||
|
||||
context.set_current_function(self.identifier.as_str(), Some(self.location.line))?;
|
||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||
|
||||
let r#return = context.current_function().borrow().r#return();
|
||||
match r#return {
|
||||
revive_llvm_context::PolkaVMFunctionReturn::None => {}
|
||||
revive_llvm_context::PolkaVMFunctionReturn::Primitive { pointer } => {
|
||||
let identifier = self.result.pop().expect("Always exists");
|
||||
|
||||
let r#type = identifier.r#type.unwrap_or_default();
|
||||
context.build_store(pointer, r#type.into_llvm(context).const_zero())?;
|
||||
context
|
||||
@@ -288,6 +290,8 @@ where
|
||||
}
|
||||
|
||||
self.body.into_llvm(context)?;
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
|
||||
match context
|
||||
.basic_block()
|
||||
.get_last_instruction()
|
||||
@@ -314,6 +318,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
context.pop_debug_scope();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use inkwell::debug_info::AsDIScope;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -217,16 +219,30 @@ where
|
||||
}
|
||||
|
||||
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
||||
if let Some(debug_info) = context.debug_info() {
|
||||
let di_builder = debug_info.builder();
|
||||
let object_name: &str = self.identifier.as_str();
|
||||
let di_parent_scope = debug_info
|
||||
.top_scope()
|
||||
.expect("expected an existing debug-info scope");
|
||||
let object_scope = di_builder.create_namespace(di_parent_scope, object_name, true);
|
||||
context.push_debug_scope(object_scope.as_debug_info_scope());
|
||||
}
|
||||
|
||||
if self.identifier.ends_with("_deployed") {
|
||||
revive_llvm_context::PolkaVMImmutableDataLoadFunction.into_llvm(context)?;
|
||||
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(self.code).into_llvm(context)?;
|
||||
} else {
|
||||
revive_llvm_context::PolkaVMDeployCodeFunction::new(self.code).into_llvm(context)?;
|
||||
}
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
|
||||
if let Some(object) = self.inner_object {
|
||||
object.into_llvm(context)?;
|
||||
}
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
|
||||
context.pop_debug_scope();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -101,7 +101,9 @@ where
|
||||
) -> anyhow::Result<()> {
|
||||
if self.bindings.len() == 1 {
|
||||
let identifier = self.bindings.remove(0);
|
||||
let r#type = identifier.r#type.unwrap_or_default().into_llvm(context);
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
let identifier_type = identifier.r#type.clone().unwrap_or_default();
|
||||
let r#type = identifier_type.into_llvm(context);
|
||||
let pointer = context.build_alloca(r#type, identifier.inner.as_str());
|
||||
context
|
||||
.current_function()
|
||||
@@ -116,7 +118,7 @@ where
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.yul_mut()
|
||||
.insert_constant(identifier.inner, constant);
|
||||
.insert_constant(identifier.inner.clone(), constant);
|
||||
}
|
||||
|
||||
value.to_llvm()
|
||||
@@ -131,6 +133,8 @@ where
|
||||
}
|
||||
|
||||
for (index, binding) in self.bindings.iter().enumerate() {
|
||||
context.set_debug_location(self.location.line, 0, None)?;
|
||||
|
||||
let yul_type = binding
|
||||
.r#type
|
||||
.to_owned()
|
||||
|
||||
Reference in New Issue
Block a user