use std::cell::RefCell;
use revive_common::BIT_LENGTH_WORD;
use inkwell::debug_info::AsDIScope;
use inkwell::debug_info::DIScope;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScopeStack<'ctx> {
stack: Vec<DIScope<'ctx>>,
}
impl<'ctx> ScopeStack<'ctx> {
pub fn from(item: DIScope<'ctx>) -> Self {
Self { stack: vec![item] }
}
pub fn top(&self) -> Option<DIScope<'ctx>> {
self.stack.last().copied()
}
pub fn push(&mut self, scope: DIScope<'ctx>) {
self.stack.push(scope)
}
pub fn pop(&mut self) -> Option<DIScope<'ctx>> {
self.stack.pop()
}
pub fn len(&self) -> usize {
self.stack.len()
}
}
pub struct DebugInfo<'ctx> {
compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
builder: inkwell::debug_info::DebugInfoBuilder<'ctx>,
scope_stack: RefCell<ScopeStack<'ctx>>,
}
impl<'ctx> DebugInfo<'ctx> {
pub fn new(
module: &inkwell::module::Module<'ctx>,
debug_config: &crate::debug_config::DebugConfig,
) -> Self {
let module_name = module.get_name().to_string_lossy();
let yul_name = debug_config
.contract_path
.as_ref()
.map(|path| path.display().to_string());
let (builder, compile_unit) = module.create_debug_info_builder(
true,
inkwell::debug_info::DWARFSourceLanguage::C,
yul_name.as_deref().unwrap_or_else(|| module_name.as_ref()),
"",
"",
false,
"",
0,
"",
inkwell::debug_info::DWARFEmissionKind::Full,
0,
false,
false,
"",
"",
);
Self {
compile_unit,
builder,
scope_stack: RefCell::new(ScopeStack::from(compile_unit.as_debug_info_scope())),
}
}
pub fn initialize_module(
&self,
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
) {
let debug_metadata_value = llvm
.i32_type()
.const_int(inkwell::debug_info::debug_metadata_version() as u64, false);
module.add_basic_value_flag(
"Debug Info Version",
inkwell::module::FlagBehavior::Warning,
debug_metadata_value,
);
self.push_scope(self.compilation_unit().get_file().as_debug_info_scope());
}
pub fn finalize_module(&self) {
self.builder().finalize()
}
pub fn create_function(
&self,
name: &str,
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
let flags = inkwell::debug_info::DIFlagsConstants::ZERO;
let subroutine_type = self.builder.create_subroutine_type(
self.compile_unit.get_file(),
Some(self.create_word_type(Some(flags))?.as_type()),
&[],
flags,
);
let function = self.builder.create_function(
self.compile_unit.get_file().as_debug_info_scope(),
name,
None,
self.compile_unit.get_file(),
42,
subroutine_type,
true,
false,
1,
flags,
false,
);
self.builder.create_lexical_block(
function.as_debug_info_scope(),
self.compile_unit.get_file(),
1,
1,
);
Ok(function)
}
pub fn create_primitive_type(
&self,
bit_length: usize,
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
let di_flags = flags.unwrap_or(inkwell::debug_info::DIFlagsConstants::ZERO);
let di_encoding: u32 = 0;
let type_name = String::from("U") + bit_length.to_string().as_str();
self.builder
.create_basic_type(type_name.as_str(), bit_length as u64, di_encoding, di_flags)
.map_err(|error| anyhow::anyhow!("Debug info error: {}", error))
}
pub fn create_word_type(
&self,
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
self.create_primitive_type(BIT_LENGTH_WORD, flags)
}
pub fn builder(&self) -> &inkwell::debug_info::DebugInfoBuilder<'ctx> {
&self.builder
}
pub fn compilation_unit(&self) -> &inkwell::debug_info::DICompileUnit<'ctx> {
&self.compile_unit
}
pub fn push_scope(&self, scope: DIScope<'ctx>) {
self.scope_stack.borrow_mut().push(scope)
}
pub fn pop_scope(&self) -> Option<DIScope<'ctx>> {
self.scope_stack.borrow_mut().pop()
}
pub fn top_scope(&self) -> Option<DIScope<'ctx>> {
self.scope_stack.borrow().top()
}
pub fn num_scopes(&self) -> usize {
self.scope_stack.borrow().len()
}
}