use crate::module::MetadataId;
use crate::{Result, error};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DebugNode {
Location {
line: u32,
column: u32,
scope: MetadataId,
inlined_at: Option<MetadataId>,
},
Generic {
tag: u32,
header: Option<MetadataId>,
operands: Vec<Option<MetadataId>>,
},
Subrange {
count: i64,
lower_bound: i64,
},
Enumerator {
value: i64,
name: Option<MetadataId>,
},
BasicType {
tag: u32,
name: Option<MetadataId>,
size: u64,
align: u64,
encoding: u32,
},
DerivedType {
tag: u32,
name: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
scope: Option<MetadataId>,
base_type: Option<MetadataId>,
size: u64,
align: u64,
offset: u64,
flags: u32,
extra_data: Option<MetadataId>,
},
CompositeType {
tag: u32,
name: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
scope: Option<MetadataId>,
base_type: Option<MetadataId>,
size: u64,
align: u64,
offset: u64,
flags: u32,
elements: Option<MetadataId>,
runtime_lang: u32,
vtable_holder: Option<MetadataId>,
template_params: Option<MetadataId>,
identifier: Option<MetadataId>,
},
SubroutineType {
flags: u32,
types: Option<MetadataId>,
},
File {
filename: Option<MetadataId>,
directory: Option<MetadataId>,
},
CompileUnit {
language: u32,
file: Option<MetadataId>,
producer: Option<MetadataId>,
optimized: bool,
flags: Option<MetadataId>,
runtime_version: u32,
split_debug_filename: Option<MetadataId>,
emission_kind: u32,
enums: Option<MetadataId>,
retained_types: Option<MetadataId>,
subprograms: Option<MetadataId>,
globals: Option<MetadataId>,
imports: Option<MetadataId>,
dwo_id: u64,
},
Subprogram {
scope: Option<MetadataId>,
name: Option<MetadataId>,
linkage_name: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
ty: Option<MetadataId>,
local: bool,
definition: bool,
scope_line: u32,
containing_type: Option<MetadataId>,
virtuality: u32,
virtual_index: u32,
flags: u32,
optimized: bool,
function: Option<MetadataId>,
template_params: Option<MetadataId>,
declaration: Option<MetadataId>,
variables: Option<MetadataId>,
},
LexicalBlock {
scope: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
column: u32,
},
LexicalBlockFile {
scope: Option<MetadataId>,
file: Option<MetadataId>,
discriminator: u32,
},
Namespace {
scope: Option<MetadataId>,
file: Option<MetadataId>,
name: Option<MetadataId>,
line: u32,
},
Module {
scope: Option<MetadataId>,
name: Option<MetadataId>,
config_macros: Option<MetadataId>,
include_path: Option<MetadataId>,
isysroot: Option<MetadataId>,
},
TemplateType {
name: Option<MetadataId>,
ty: Option<MetadataId>,
},
TemplateValue {
tag: u32,
name: Option<MetadataId>,
ty: Option<MetadataId>,
value: Option<MetadataId>,
},
GlobalVariable {
scope: Option<MetadataId>,
name: Option<MetadataId>,
linkage_name: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
ty: Option<MetadataId>,
local: bool,
definition: bool,
variable: Option<MetadataId>,
declaration: Option<MetadataId>,
},
LocalVariable {
tag: u32,
scope: Option<MetadataId>,
name: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
ty: Option<MetadataId>,
arg: u32,
flags: u32,
},
Expression(Vec<u64>),
ObjcProperty {
name: Option<MetadataId>,
file: Option<MetadataId>,
line: u32,
getter: Option<MetadataId>,
setter: Option<MetadataId>,
attributes: u32,
ty: Option<MetadataId>,
},
ImportedEntity {
tag: u32,
scope: Option<MetadataId>,
entity: Option<MetadataId>,
line: u32,
name: Option<MetadataId>,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DebugLoc {
pub line: u32,
pub column: u32,
pub scope: Option<MetadataId>,
pub inlined_at: Option<MetadataId>,
}
pub(crate) fn unsigned(v: u64) -> u32 {
u32::try_from(v & 0xFFFF_FFFF).unwrap_or(0)
}
pub(crate) fn column(column: u32) -> u32 {
if column >= 1 << 16 { 0 } else { column }
}
impl DebugNode {
pub fn operands(&self) -> Vec<Option<MetadataId>> {
use DebugNode as N;
match self {
N::Location {
scope, inlined_at, ..
} => {
let mut ops = vec![Some(*scope)];
if inlined_at.is_some() {
ops.push(*inlined_at);
}
ops
}
N::Generic { header, operands, .. } => {
let mut ops = vec![*header];
ops.extend(operands);
ops
}
N::Subrange { .. } | N::Expression(_) => Vec::new(),
N::Enumerator { name, .. } => vec![*name],
N::BasicType { name, .. } => vec![None, None, *name],
N::DerivedType {
file,
scope,
name,
base_type,
extra_data,
..
} => {
vec![*file, *scope, *name, *base_type, *extra_data]
}
N::CompositeType {
file,
scope,
name,
base_type,
elements,
vtable_holder,
template_params,
identifier,
..
} => vec![
*file,
*scope,
*name,
*base_type,
*elements,
*vtable_holder,
*template_params,
*identifier,
],
N::SubroutineType { types, .. } => vec![None, None, None, None, *types, None, None, None],
N::File { filename, directory } => vec![*filename, *directory],
N::CompileUnit {
file,
producer,
flags,
split_debug_filename,
enums,
retained_types,
subprograms,
globals,
imports,
..
} => vec![
*file,
*producer,
*flags,
*split_debug_filename,
*enums,
*retained_types,
*subprograms,
*globals,
*imports,
],
N::Subprogram {
file,
scope,
name,
linkage_name,
ty,
containing_type,
function,
template_params,
declaration,
variables,
..
} => vec![
*file,
*scope,
*name,
*name,
*linkage_name,
*ty,
*containing_type,
*function,
*template_params,
*declaration,
*variables,
],
N::LexicalBlock { file, scope, .. } | N::LexicalBlockFile { file, scope, .. } => {
vec![*file, *scope]
}
N::Namespace {
file, scope, name, ..
} => vec![*file, *scope, *name],
N::Module {
scope,
name,
config_macros,
include_path,
isysroot,
} => {
vec![*scope, *name, *config_macros, *include_path, *isysroot]
}
N::TemplateType { name, ty } => vec![*name, *ty],
N::TemplateValue { name, ty, value, .. } => vec![*name, *ty, *value],
N::GlobalVariable {
scope,
name,
file,
ty,
linkage_name,
variable,
declaration,
..
} => {
vec![
*scope,
*name,
*file,
*ty,
*name,
*linkage_name,
*variable,
*declaration,
]
}
N::LocalVariable {
scope,
name,
file,
ty,
..
} => vec![*scope, *name, *file, *ty],
N::ObjcProperty {
name,
file,
getter,
setter,
ty,
..
} => vec![*name, *file, *getter, *setter, *ty],
N::ImportedEntity {
scope, entity, name, ..
} => vec![*scope, *entity, *name],
}
}
pub fn scope(&self) -> Option<MetadataId> {
use DebugNode as N;
match self {
N::Location { scope, .. } => Some(*scope),
N::LexicalBlock { scope, .. }
| N::LexicalBlockFile { scope, .. }
| N::LocalVariable { scope, .. }
| N::Subprogram { scope, .. } => *scope,
_ => None,
}
}
}
pub(crate) fn read(code: u32, ops: &[u64], at: usize) -> Result<(bool, DebugNode)> {
let n = |i: usize| -> Result<u64> {
ops.get(i)
.copied()
.ok_or_else(|| error(at, format!("metadata record {code} lacks operand {i}")))
};
let u = |i: usize| -> Result<u32> { n(i).map(unsigned) };
let id = |v: u64| -> Result<MetadataId> { usize::try_from(v).map_err(|_| error(at, "metadata id")) };
let md = |i: usize| -> Result<Option<MetadataId>> {
match n(i)? {
0 => Ok(None),
v => id(v.saturating_sub(1)).map(Some),
}
};
let length = |want: &[usize]| -> Result<()> {
if want.contains(&ops.len()) {
Ok(())
} else {
Err(error(
at,
format!("metadata record {code} of {} operands", ops.len()),
))
}
};
let distinct = n(0)? != 0;
use DebugNode as N;
let node = match code {
7 => {
length(&[5])?;
N::Location {
line: u(1)?,
column: column(u(2)?),
scope: id(n(3)?)?,
inlined_at: md(4)?,
}
}
12 => {
if ops.len() < 4 {
return Err(error(
at,
format!("metadata record {code} of {} operands", ops.len()),
));
}
let tag = u16::try_from(n(1)?).ok().filter(|_| n(2).is_ok_and(|v| v == 0));
let Some(tag) = tag else {
return Err(error(at, "a generic debug node of an unknown tag or version"));
};
N::Generic {
tag: u32::from(tag),
header: md(3)?,
operands: (4..ops.len()).map(md).collect::<Result<_>>()?,
}
}
13 => {
length(&[3])?;
N::Subrange {
count: n(1)? as i64,
lower_bound: unrotate(n(2)?),
}
}
14 => {
length(&[3])?;
N::Enumerator {
value: unrotate(n(1)?),
name: md(2)?,
}
}
15 => {
length(&[6])?;
N::BasicType {
tag: u(1)?,
name: md(2)?,
size: n(3)?,
align: n(4)?,
encoding: u(5)?,
}
}
17 => {
length(&[12])?;
N::DerivedType {
tag: u(1)?,
name: md(2)?,
file: md(3)?,
line: u(4)?,
scope: md(5)?,
base_type: md(6)?,
size: n(7)?,
align: n(8)?,
offset: n(9)?,
flags: u(10)?,
extra_data: md(11)?,
}
}
18 => {
length(&[16])?;
N::CompositeType {
tag: u(1)?,
name: md(2)?,
file: md(3)?,
line: u(4)?,
scope: md(5)?,
base_type: md(6)?,
size: n(7)?,
align: n(8)?,
offset: n(9)?,
flags: u(10)?,
elements: md(11)?,
runtime_lang: u(12)?,
vtable_holder: md(13)?,
template_params: md(14)?,
identifier: md(15)?,
}
}
19 => {
length(&[3])?;
N::SubroutineType {
flags: u(1)?,
types: md(2)?,
}
}
32 => {
length(&[6])?;
N::Module {
scope: md(1)?,
name: md(2)?,
config_macros: md(3)?,
include_path: md(4)?,
isysroot: md(5)?,
}
}
16 => {
length(&[3])?;
N::File {
filename: md(1)?,
directory: md(2)?,
}
}
20 => {
length(&[14, 15])?;
N::CompileUnit {
language: u(1)?,
file: md(2)?,
producer: md(3)?,
optimized: n(4)? != 0,
flags: md(5)?,
runtime_version: u(6)?,
split_debug_filename: md(7)?,
emission_kind: u(8)?,
enums: md(9)?,
retained_types: md(10)?,
subprograms: md(11)?,
globals: md(12)?,
imports: md(13)?,
dwo_id: ops.get(14).copied().unwrap_or(0),
}
}
21 => {
length(&[19])?;
N::Subprogram {
scope: md(1)?,
name: md(2)?,
linkage_name: md(3)?,
file: md(4)?,
line: u(5)?,
ty: md(6)?,
local: n(7)? != 0,
definition: n(8)? != 0,
scope_line: u(9)?,
containing_type: md(10)?,
virtuality: u(11)?,
virtual_index: u(12)?,
flags: u(13)?,
optimized: n(14)? != 0,
function: md(15)?,
template_params: md(16)?,
declaration: md(17)?,
variables: md(18)?,
}
}
22 => {
length(&[5])?;
N::LexicalBlock {
scope: md(1)?,
file: md(2)?,
line: u(3)?,
column: u(4)?,
}
}
23 => {
length(&[4])?;
N::LexicalBlockFile {
scope: md(1)?,
file: md(2)?,
discriminator: u(3)?,
}
}
24 => {
length(&[5])?;
N::Namespace {
scope: md(1)?,
file: md(2)?,
name: md(3)?,
line: u(4)?,
}
}
25 => {
length(&[3])?;
N::TemplateType {
name: md(1)?,
ty: md(2)?,
}
}
26 => {
length(&[5])?;
N::TemplateValue {
tag: u(1)?,
name: md(2)?,
ty: md(3)?,
value: md(4)?,
}
}
27 => {
length(&[11])?;
N::GlobalVariable {
scope: md(1)?,
name: md(2)?,
linkage_name: md(3)?,
file: md(4)?,
line: u(5)?,
ty: md(6)?,
local: n(7)? != 0,
definition: n(8)? != 0,
variable: md(9)?,
declaration: md(10)?,
}
}
28 => {
length(&[9, 10])?;
N::LocalVariable {
tag: u(1)?,
scope: md(2)?,
name: md(3)?,
file: md(4)?,
line: u(5)?,
ty: md(6)?,
arg: u(7)?,
flags: u(8)?,
}
}
29 => N::Expression(ops.get(1..).unwrap_or_default().to_vec()),
30 => {
length(&[8])?;
N::ObjcProperty {
name: md(1)?,
file: md(2)?,
line: u(3)?,
getter: md(4)?,
setter: md(5)?,
attributes: u(6)?,
ty: md(7)?,
}
}
31 => {
length(&[6])?;
N::ImportedEntity {
tag: u(1)?,
scope: md(2)?,
entity: md(3)?,
line: u(4)?,
name: md(5)?,
}
}
other => {
return Err(error(
at,
format!("metadata record {other} is not debug information"),
));
}
};
Ok((distinct, node))
}
fn unrotate(v: u64) -> i64 {
let magnitude = (v >> 1) as i64;
if v & 1 == 0 { magnitude } else { !magnitude }
}