use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::u32;
use super::ast::*;
use super::CLangStandard;
pub type DeclID = u64;
pub type TypeID = u64;
pub type IdentID = u64;
pub type StmtID = u64;
#[derive(Debug, Clone, Default)]
pub struct ImportRemapping {
pub decl_map: BTreeMap<DeclID, Decl>,
pub type_map: BTreeMap<TypeID, QualType>,
pub ident_map: BTreeMap<IdentID, String>,
pub reverse_decl: HashMap<String, DeclID>,
pub reverse_type: HashMap<String, TypeID>,
pub pending_imports: VecDeque<PendingImport>,
}
#[derive(Debug, Clone)]
pub struct PendingImport {
pub source_decl_id: DeclID,
pub kind: PendingImportKind,
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PendingImportKind {
ForwardDecl,
UnresolvedTemplate,
ODRCandidate,
TypeDependency,
}
impl ImportRemapping {
pub fn new() -> Self {
Self::default()
}
pub fn register_decl(&mut self, source_id: DeclID, decl: Decl) {
if let Some(name) = decl.name() {
self.reverse_decl.insert(name.to_string(), source_id);
}
self.decl_map.insert(source_id, decl);
}
pub fn find_decl(&self, source_id: DeclID) -> Option<&Decl> {
self.decl_map.get(&source_id)
}
pub fn find_decl_id(&self, name: &str) -> Option<DeclID> {
self.reverse_decl.get(name).copied()
}
pub fn register_type(&mut self, source_id: TypeID, ty: QualType) {
let fingerprint = type_fingerprint(&ty);
self.reverse_type.insert(fingerprint, source_id);
self.type_map.insert(source_id, ty);
}
pub fn find_type(&self, source_id: TypeID) -> Option<&QualType> {
self.type_map.get(&source_id)
}
pub fn has_type(&self, ty: &QualType) -> bool {
let fingerprint = type_fingerprint(ty);
self.reverse_type.contains_key(&fingerprint)
}
}
pub fn type_fingerprint(ty: &QualType) -> String {
let mut s = String::new();
if ty.is_const {
s.push_str("const ");
}
if ty.is_volatile {
s.push_str("volatile ");
}
if ty.is_restrict {
s.push_str("restrict ");
}
type_node_fingerprint(&ty.base, &mut s);
s
}
fn type_node_fingerprint(ty: &TypeNode, s: &mut String) {
match ty {
TypeNode::Void => s.push_str("void"),
TypeNode::Char => s.push_str("char"),
TypeNode::SChar => s.push_str("schar"),
TypeNode::UChar => s.push_str("uchar"),
TypeNode::Short => s.push_str("short"),
TypeNode::UShort => s.push_str("ushort"),
TypeNode::Int => s.push_str("int"),
TypeNode::UInt => s.push_str("uint"),
TypeNode::Long => s.push_str("long"),
TypeNode::ULong => s.push_str("ulong"),
TypeNode::LongLong => s.push_str("llong"),
TypeNode::ULongLong => s.push_str("ullong"),
TypeNode::Float => s.push_str("float"),
TypeNode::Double => s.push_str("double"),
TypeNode::LongDouble => s.push_str("ldouble"),
TypeNode::Bool => s.push_str("bool"),
TypeNode::Complex => s.push_str("complex"),
TypeNode::Auto => s.push_str("auto"),
TypeNode::Pointer(inner) => {
s.push('*');
type_fingerprint_inner(inner, s);
}
TypeNode::Array { elem, size } => {
s.push('[');
if let Some(sz) = size {
s.push_str(&sz.to_string());
}
s.push(']');
type_fingerprint_inner(elem, s);
}
TypeNode::Function {
ret,
params,
is_vararg,
} => {
s.push('(');
for p in params {
type_fingerprint_inner(p, s);
s.push(',');
}
if *is_vararg {
s.push_str("...");
}
s.push_str(")->");
type_fingerprint_inner(ret, s);
}
TypeNode::Struct {
name,
fields,
is_union,
} => {
if *is_union {
s.push_str("union{");
} else {
s.push_str("struct{");
}
if let Some(n) = name {
s.push_str(n);
s.push(':');
}
for f in fields {
type_fingerprint_inner(&f.ty, s);
s.push(';');
}
s.push('}');
}
TypeNode::Enum { name, variants } => {
s.push_str("enum{");
if let Some(n) = name {
s.push_str(n);
s.push(':');
}
for v in variants {
s.push_str(&v.name);
if let Some(val) = v.value {
s.push('=');
s.push_str(&val.to_string());
}
s.push(',');
}
s.push('}');
}
TypeNode::Typedef { name, underlying } => {
s.push_str("typedef ");
s.push_str(name);
s.push(':');
type_fingerprint_inner(underlying, s);
}
TypeNode::Record(name) => {
s.push_str("record:");
s.push_str(name);
}
}
}
fn type_fingerprint_inner(ty: &QualType, s: &mut String) {
if ty.is_const {
s.push_str("c");
}
if ty.is_volatile {
s.push_str("v");
}
if ty.is_restrict {
s.push_str("r");
}
type_node_fingerprint(&ty.base, s);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EquivalenceResult {
Equivalent,
NotEquivalent(String),
Indeterminate(String),
}
pub struct StructuralEquivChecker {
equiv_cache: HashSet<(String, String)>,
not_equiv_cache: HashSet<(String, String)>,
max_depth: usize,
odr_mode: bool,
}
impl StructuralEquivChecker {
pub fn new(odr_mode: bool) -> Self {
Self {
equiv_cache: HashSet::new(),
not_equiv_cache: HashSet::new(),
max_depth: 32,
odr_mode,
}
}
pub fn are_equivalent(&mut self, a: &QualType, b: &QualType) -> EquivalenceResult {
let fp_a = type_fingerprint(a);
let fp_b = type_fingerprint(b);
if self.equiv_cache.contains(&(fp_a.clone(), fp_b.clone()))
|| self.equiv_cache.contains(&(fp_b.clone(), fp_a.clone()))
{
return EquivalenceResult::Equivalent;
}
if self.not_equiv_cache.contains(&(fp_a.clone(), fp_b.clone()))
|| self.not_equiv_cache.contains(&(fp_b.clone(), fp_a.clone()))
{
return EquivalenceResult::NotEquivalent("cached mismatch".into());
}
let result = self.check_types(a, b, 0);
match &result {
EquivalenceResult::Equivalent => {
self.equiv_cache.insert((fp_a, fp_b));
}
EquivalenceResult::NotEquivalent(_) => {
self.not_equiv_cache.insert((fp_a, fp_b));
}
_ => {}
}
result
}
fn check_types(&self, a: &QualType, b: &QualType, depth: usize) -> EquivalenceResult {
if depth > self.max_depth {
return EquivalenceResult::Indeterminate("max recursion depth exceeded".into());
}
if a.is_const != b.is_const {
if !self.odr_mode || !b.is_const {
return EquivalenceResult::NotEquivalent("const qualifier mismatch".into());
}
}
if a.is_volatile != b.is_volatile {
if !self.odr_mode || !b.is_volatile {
return EquivalenceResult::NotEquivalent("volatile qualifier mismatch".into());
}
}
if a.is_restrict != b.is_restrict {
return EquivalenceResult::NotEquivalent("restrict qualifier mismatch".into());
}
self.check_type_nodes(&a.base, &b.base, depth + 1)
}
fn check_type_nodes(&self, a: &TypeNode, b: &TypeNode, depth: usize) -> EquivalenceResult {
match (a, b) {
(TypeNode::Void, TypeNode::Void)
| (TypeNode::Char, TypeNode::Char)
| (TypeNode::SChar, TypeNode::SChar)
| (TypeNode::UChar, TypeNode::UChar)
| (TypeNode::Short, TypeNode::Short)
| (TypeNode::UShort, TypeNode::UShort)
| (TypeNode::Int, TypeNode::Int)
| (TypeNode::UInt, TypeNode::UInt)
| (TypeNode::Long, TypeNode::Long)
| (TypeNode::ULong, TypeNode::ULong)
| (TypeNode::LongLong, TypeNode::LongLong)
| (TypeNode::ULongLong, TypeNode::ULongLong)
| (TypeNode::Float, TypeNode::Float)
| (TypeNode::Double, TypeNode::Double)
| (TypeNode::LongDouble, TypeNode::LongDouble)
| (TypeNode::Bool, TypeNode::Bool)
| (TypeNode::Auto, TypeNode::Auto) => EquivalenceResult::Equivalent,
(TypeNode::Pointer(inner_a), TypeNode::Pointer(inner_b)) => {
self.check_types(inner_a, inner_b, depth)
}
(TypeNode::Array { elem: ea, size: sa }, TypeNode::Array { elem: eb, size: sb }) => {
if sa != sb {
return EquivalenceResult::NotEquivalent(format!(
"array size mismatch: {:?} vs {:?}",
sa, sb
));
}
self.check_types(ea, eb, depth)
}
(
TypeNode::Function {
ret: ra,
params: pa,
is_vararg: va,
},
TypeNode::Function {
ret: rb,
params: pb,
is_vararg: vb,
},
) => {
if va != vb {
return EquivalenceResult::NotEquivalent("vararg flag mismatch".into());
}
if pa.len() != pb.len() {
return EquivalenceResult::NotEquivalent(format!(
"parameter count mismatch: {} vs {}",
pa.len(),
pb.len()
));
}
let ret_result = self.check_types(ra, rb, depth);
if !matches!(ret_result, EquivalenceResult::Equivalent) {
return ret_result;
}
for (p_a, p_b) in pa.iter().zip(pb.iter()) {
let param_result = self.check_types(p_a, p_b, depth);
if !matches!(param_result, EquivalenceResult::Equivalent) {
return param_result;
}
}
EquivalenceResult::Equivalent
}
(
TypeNode::Struct {
name: na,
fields: fa,
is_union: ua,
},
TypeNode::Struct {
name: nb,
fields: fb,
is_union: ub,
},
) => {
if ua != ub {
return EquivalenceResult::NotEquivalent("struct/union tag mismatch".into());
}
if na.is_some() && nb.is_some() && na != nb {
return EquivalenceResult::NotEquivalent(format!(
"struct name mismatch: {:?} vs {:?}",
na, nb
));
}
if fa.len() != fb.len() {
return EquivalenceResult::NotEquivalent(format!(
"field count mismatch: {} vs {}",
fa.len(),
fb.len()
));
}
for (f_a, f_b) in fa.iter().zip(fb.iter()) {
if f_a.name != f_b.name {
return EquivalenceResult::NotEquivalent(format!(
"field name mismatch: {} vs {}",
f_a.name, f_b.name
));
}
let field_result = self.check_types(&f_a.ty, &f_b.ty, depth);
if !matches!(field_result, EquivalenceResult::Equivalent) {
return field_result;
}
if f_a.bit_width != f_b.bit_width {
return EquivalenceResult::NotEquivalent("bit-field width mismatch".into());
}
}
EquivalenceResult::Equivalent
}
(
TypeNode::Enum {
name: na,
variants: va,
},
TypeNode::Enum {
name: nb,
variants: vb,
},
) => {
if na.is_some() && nb.is_some() && na != nb {
return EquivalenceResult::NotEquivalent(format!(
"enum name mismatch: {:?} vs {:?}",
na, nb
));
}
if va.len() != vb.len() {
return EquivalenceResult::NotEquivalent("enum variant count mismatch".into());
}
for (v_a, v_b) in va.iter().zip(vb.iter()) {
if v_a.name != v_b.name {
return EquivalenceResult::NotEquivalent(format!(
"enumerator name mismatch: {} vs {}",
v_a.name, v_b.name
));
}
if v_a.value != v_b.value {
return EquivalenceResult::NotEquivalent(format!(
"enumerator value mismatch: {:?} vs {:?}",
v_a.value, v_b.value
));
}
}
EquivalenceResult::Equivalent
}
(
TypeNode::Typedef {
name: _,
underlying: ua,
},
TypeNode::Typedef {
name: _,
underlying: ub,
},
) => self.check_types(ua, ub, depth),
(TypeNode::Record(na), TypeNode::Record(nb)) => {
if na == nb {
EquivalenceResult::Equivalent
} else {
EquivalenceResult::NotEquivalent(format!(
"record name mismatch: {} vs {}",
na, nb
))
}
}
(TypeNode::Complex, TypeNode::Complex) => EquivalenceResult::Equivalent,
(TypeNode::Auto, _) | (_, TypeNode::Auto) => EquivalenceResult::Equivalent,
_ => EquivalenceResult::NotEquivalent(format!(
"type kind mismatch: {:?} vs {:?}",
a.kind_name(),
b.kind_name()
)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeclRelationship {
Identical,
Redeclaration,
ODREquivalent,
ODRConflict(String),
Unrelated,
}
pub struct DeclComparator {
type_checker: StructuralEquivChecker,
standard: CLangStandard,
}
impl DeclComparator {
pub fn new(standard: CLangStandard) -> Self {
Self {
type_checker: StructuralEquivChecker::new(true),
standard,
}
}
pub fn compare(&mut self, a: &Decl, b: &Decl) -> DeclRelationship {
match (a, b) {
(Decl::Function(fa), Decl::Function(fb)) => self.compare_functions(fa, fb),
(Decl::Variable(va), Decl::Variable(vb)) => self.compare_variables(va, vb),
(Decl::Typedef(ta), Decl::Typedef(tb)) => self.compare_typedefs(ta, tb),
(Decl::Struct(sa), Decl::Struct(sb)) => self.compare_structs(sa, sb),
(Decl::Enum(ea), Decl::Enum(eb)) => self.compare_enums(ea, eb),
_ => DeclRelationship::Unrelated,
}
}
fn compare_functions(&mut self, a: &FunctionDecl, b: &FunctionDecl) -> DeclRelationship {
if a.name != b.name {
return DeclRelationship::Unrelated;
}
let ret_equiv = self.type_checker.are_equivalent(&a.ret_ty, &b.ret_ty);
if !matches!(ret_equiv, EquivalenceResult::Equivalent) {
return DeclRelationship::ODRConflict(format!(
"return type mismatch: {} vs {}",
a.ret_ty, b.ret_ty
));
}
if a.params.len() != b.params.len() {
return DeclRelationship::ODRConflict(format!(
"parameter count mismatch: {} vs {}",
a.params.len(),
b.params.len()
));
}
if a.is_vararg != b.is_vararg {
return DeclRelationship::ODRConflict("vararg flag mismatch".into());
}
for (pa, pb) in a.params.iter().zip(b.params.iter()) {
let param_equiv = self.type_checker.are_equivalent(&pa.ty, &pb.ty);
if !matches!(param_equiv, EquivalenceResult::Equivalent) {
return DeclRelationship::ODRConflict(format!(
"parameter type mismatch: {} vs {}",
pa.ty, pb.ty
));
}
}
if a.body.is_some() && b.body.is_some() {
return DeclRelationship::ODRConflict("multiple function definitions".into());
}
if a.body.is_some() || b.body.is_some() {
return DeclRelationship::Redeclaration;
}
DeclRelationship::ODREquivalent
}
fn compare_variables(&mut self, a: &VarDecl, b: &VarDecl) -> DeclRelationship {
if a.name != b.name {
return DeclRelationship::Unrelated;
}
let type_equiv = self.type_checker.are_equivalent(&a.ty, &b.ty);
if !matches!(type_equiv, EquivalenceResult::Equivalent) {
return DeclRelationship::ODRConflict(format!(
"variable type mismatch: {} vs {}",
a.ty, b.ty
));
}
if a.init.is_some() && b.init.is_some() {
if a.is_global && b.is_global {
return DeclRelationship::ODRConflict("multiple variable definitions".into());
}
}
if a.init.is_some() || b.init.is_some() {
return DeclRelationship::Redeclaration;
}
DeclRelationship::ODREquivalent
}
fn compare_typedefs(&mut self, a: &TypedefDecl, b: &TypedefDecl) -> DeclRelationship {
if a.name != b.name {
return DeclRelationship::Unrelated;
}
let type_equiv = self
.type_checker
.are_equivalent(&a.underlying, &b.underlying);
if !matches!(type_equiv, EquivalenceResult::Equivalent) {
return DeclRelationship::ODRConflict(format!(
"typedef underlying type mismatch: {} vs {}",
a.underlying, b.underlying
));
}
DeclRelationship::Identical
}
fn compare_structs(&mut self, a: &StructDecl, b: &StructDecl) -> DeclRelationship {
let name_a = a.name.as_deref().unwrap_or("");
let name_b = b.name.as_deref().unwrap_or("");
if !name_a.is_empty() && !name_b.is_empty() && name_a != name_b {
return DeclRelationship::Unrelated;
}
if a.is_union != b.is_union {
return DeclRelationship::ODRConflict("struct/union tag mismatch".into());
}
if a.fields.len() != b.fields.len() {
return DeclRelationship::ODRConflict(format!(
"field count mismatch: {} vs {}",
a.fields.len(),
b.fields.len()
));
}
for (fa, fb) in a.fields.iter().zip(b.fields.iter()) {
if fa.name != fb.name {
return DeclRelationship::ODRConflict(format!(
"field name mismatch: {} vs {}",
fa.name, fb.name
));
}
let field_equiv = self.type_checker.are_equivalent(&fa.ty, &fb.ty);
if !matches!(field_equiv, EquivalenceResult::Equivalent) {
return DeclRelationship::ODRConflict(format!(
"field type mismatch: {} vs {}",
fa.ty, fb.ty
));
}
}
if !a.fields.is_empty() && b.fields.is_empty() {
return DeclRelationship::Redeclaration;
}
if a.fields.is_empty() && !b.fields.is_empty() {
return DeclRelationship::Redeclaration;
}
DeclRelationship::ODREquivalent
}
fn compare_enums(&mut self, a: &EnumDecl, b: &EnumDecl) -> DeclRelationship {
let name_a = a.name.as_deref().unwrap_or("");
let name_b = b.name.as_deref().unwrap_or("");
if !name_a.is_empty() && !name_b.is_empty() && name_a != name_b {
return DeclRelationship::Unrelated;
}
if a.variants.len() != b.variants.len() {
return DeclRelationship::ODRConflict(format!(
"enumerator count mismatch: {} vs {}",
a.variants.len(),
b.variants.len()
));
}
for (va, vb) in a.variants.iter().zip(b.variants.iter()) {
if va.name != vb.name {
return DeclRelationship::ODRConflict(format!(
"enumerator name mismatch: {} vs {}",
va.name, vb.name
));
}
if va.value != vb.value {
return DeclRelationship::ODRConflict(format!(
"enumerator value mismatch: {:?} vs {:?}",
va.value, vb.value
));
}
}
DeclRelationship::ODREquivalent
}
}
#[derive(Debug, Clone)]
pub struct RedeclChain {
pub canonical: Decl,
pub chain: Vec<Decl>,
pub source_locations: Vec<SourceLocation>,
pub is_complete: bool,
pub is_consistent: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SourceLocation {
pub file_id: u64,
pub line: u32,
pub column: u32,
pub offset: u64,
}
impl RedeclChain {
pub fn new(canonical: Decl) -> Self {
Self {
canonical,
chain: Vec::new(),
source_locations: Vec::new(),
is_complete: false,
is_consistent: true,
}
}
pub fn add_decl(
&mut self,
decl: Decl,
loc: SourceLocation,
comparator: &mut DeclComparator,
) -> Result<(), String> {
let relation = comparator.compare(&self.canonical, &decl);
match relation {
DeclRelationship::ODRConflict(msg) => {
self.is_consistent = false;
return Err(msg);
}
DeclRelationship::Redeclaration => {
if self.is_more_complete(&decl) {
self.canonical = decl.clone();
self.is_complete = true;
}
}
DeclRelationship::ODREquivalent | DeclRelationship::Identical => {
}
DeclRelationship::Unrelated => {
return Err("declaration is unrelated to this chain".into());
}
}
self.chain.push(decl);
self.source_locations.push(loc);
Ok(())
}
pub fn merge_chain(
&mut self,
other: &RedeclChain,
comparator: &mut DeclComparator,
) -> Result<(), String> {
for (decl, loc) in other.chain.iter().zip(other.source_locations.iter()) {
self.add_decl(decl.clone(), loc.clone(), comparator)?;
}
let relation = comparator.compare(&self.canonical, &other.canonical);
match relation {
DeclRelationship::ODRConflict(msg) => {
self.is_consistent = false;
return Err(msg);
}
DeclRelationship::Redeclaration
| DeclRelationship::ODREquivalent
| DeclRelationship::Identical => {
if self.is_more_complete(&other.canonical) {
self.canonical = other.canonical.clone();
self.is_complete = true;
}
}
DeclRelationship::Unrelated => {
return Err("chain canonical declarations are unrelated".into());
}
}
Ok(())
}
fn is_more_complete(&self, decl: &Decl) -> bool {
match decl {
Decl::Function(f) => f.body.is_some(),
Decl::Variable(v) => v.init.is_some(),
Decl::Struct(s) => !s.fields.is_empty(),
Decl::Enum(e) => !e.variants.is_empty(),
_ => true,
}
}
}
#[derive(Debug, Clone)]
pub struct TemplateSpecialization {
pub template_name: String,
pub template_args: Vec<String>,
pub decl: Decl,
pub is_explicit: bool,
pub is_partial: bool,
pub location: SourceLocation,
}
impl TemplateSpecialization {
pub fn new(
template_name: &str,
template_args: Vec<String>,
decl: Decl,
is_explicit: bool,
is_partial: bool,
) -> Self {
Self {
template_name: template_name.to_string(),
template_args,
decl,
is_explicit,
is_partial,
location: SourceLocation {
file_id: 0,
line: 0,
column: 0,
offset: 0,
},
}
}
pub fn specialization_key(&self) -> String {
let mut key = self.template_name.clone();
key.push('<');
for (i, arg) in self.template_args.iter().enumerate() {
if i > 0 {
key.push_str(", ");
}
key.push_str(arg);
}
key.push('>');
key
}
}
pub struct TemplateSpecializationMerger {
specializations: HashMap<String, TemplateSpecialization>,
comparator: DeclComparator,
odr_violations: u64,
}
impl TemplateSpecializationMerger {
pub fn new(standard: CLangStandard) -> Self {
Self {
specializations: HashMap::new(),
comparator: DeclComparator::new(standard),
odr_violations: 0,
}
}
pub fn register(&mut self, spec: TemplateSpecialization) -> Result<bool, String> {
let key = spec.specialization_key();
if let Some(existing) = self.specializations.get(&key) {
let relation = self.comparator.compare(&existing.decl, &spec.decl);
match relation {
DeclRelationship::ODRConflict(msg) => {
self.odr_violations += 1;
Err(format!(
"ODR violation in template specialization '{}': {}",
key, msg
))
}
DeclRelationship::ODREquivalent
| DeclRelationship::Identical
| DeclRelationship::Redeclaration => Ok(false),
DeclRelationship::Unrelated => {
self.odr_violations += 1;
Err(format!(
"unrelated declaration for specialization '{}'",
key
))
}
}
} else {
self.specializations.insert(key, spec);
Ok(true)
}
}
pub fn specialization_count(&self) -> usize {
self.specializations.len()
}
pub fn violation_count(&self) -> u64 {
self.odr_violations
}
pub fn iter(&self) -> impl Iterator<Item = &TemplateSpecialization> {
self.specializations.values()
}
}
pub struct X86ASTImport {
pub dest_tu: TranslationUnit,
pub remapping: ImportRemapping,
pub equiv_checker: StructuralEquivChecker,
pub comparator: DeclComparator,
pub template_merger: TemplateSpecializationMerger,
pub redecl_chains: HashMap<String, RedeclChain>,
pub standard: CLangStandard,
pub import_count: u64,
pub odr_violations: u64,
pub diagnostics: Vec<ImportDiagnostic>,
}
#[derive(Debug, Clone)]
pub struct TranslationUnit {
pub file_path: PathBuf,
pub declarations: Vec<Decl>,
pub source: String,
}
impl TranslationUnit {
pub fn new(file_path: &str) -> Self {
Self {
file_path: PathBuf::from(file_path),
declarations: Vec::new(),
source: String::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ImportDiagnostic {
pub level: ImportDiagLevel,
pub message: String,
pub source_location: Option<SourceLocation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImportDiagLevel {
Note,
Warning,
Error,
Fatal,
}
impl X86ASTImport {
pub fn new(standard: CLangStandard) -> Self {
Self {
dest_tu: TranslationUnit::new("<imported>"),
remapping: ImportRemapping::new(),
equiv_checker: StructuralEquivChecker::new(true),
comparator: DeclComparator::new(standard),
template_merger: TemplateSpecializationMerger::new(standard),
redecl_chains: HashMap::new(),
standard,
import_count: 0,
odr_violations: 0,
diagnostics: Vec::new(),
}
}
pub fn import_tu(&mut self, source: &TranslationUnit) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
for decl in &source.declarations {
match self.import_decl(decl) {
Ok(imported) => {
self.dest_tu.declarations.push(imported);
self.import_count += 1;
}
Err(msg) => {
self.diagnostics.push(ImportDiagnostic {
level: ImportDiagLevel::Error,
message: msg.clone(),
source_location: None,
});
errors.push(msg);
}
}
}
self.resolve_pending_imports(&mut errors);
self.finalize_chains(&mut errors);
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn import_decl(&mut self, decl: &Decl) -> Result<Decl, String> {
if let Some(name) = decl.name() {
if let Some(existing_id) = self.remapping.find_decl_id(name) {
if let Some(existing) = self.remapping.find_decl(existing_id) {
let relation = self.comparator.compare(existing, decl);
match relation {
DeclRelationship::Identical => {
return Ok(existing.clone());
}
DeclRelationship::Redeclaration => {
self.merge_into_chain(name, decl.clone())?;
return Ok(decl.clone());
}
DeclRelationship::ODREquivalent => {
self.merge_into_chain(name, decl.clone())?;
return Ok(decl.clone());
}
DeclRelationship::ODRConflict(msg) => {
self.odr_violations += 1;
return Err(msg);
}
DeclRelationship::Unrelated => {
}
}
}
}
}
let decl_id = self.next_decl_id();
self.remapping.register_decl(decl_id, decl.clone());
if let Some(name) = decl.name() {
let chain = RedeclChain::new(decl.clone());
self.redecl_chains.insert(name.to_string(), chain);
}
Ok(decl.clone())
}
fn next_decl_id(&self) -> DeclID {
self.remapping.decl_map.len() as DeclID + 1
}
fn merge_into_chain(&mut self, name: &str, decl: Decl) -> Result<(), String> {
let dummy_loc = SourceLocation {
file_id: 0,
line: 0,
column: 0,
offset: 0,
};
if let Some(chain) = self.redecl_chains.get_mut(name) {
chain.add_decl(decl, dummy_loc, &mut self.comparator)?;
if !chain.is_consistent {
self.odr_violations += 1;
}
} else {
let mut chain = RedeclChain::new(decl.clone());
chain.add_decl(decl, dummy_loc, &mut self.comparator)?;
self.redecl_chains.insert(name.to_string(), chain);
}
Ok(())
}
fn resolve_pending_imports(&mut self, errors: &mut Vec<String>) {
let max_iterations = 100;
let mut iteration = 0;
while let Some(pending) = self.remapping.pending_imports.pop_front() {
iteration += 1;
if iteration > max_iterations {
errors.push(format!(
"too many pending import iterations ({}); circular dependency?",
iteration
));
break;
}
match self.try_resolve_pending(&pending) {
Ok(()) => {}
Err(msg) => {
self.remapping.pending_imports.push_back(pending);
if iteration == max_iterations {
errors.push(msg);
}
}
}
}
}
fn try_resolve_pending(&mut self, pending: &PendingImport) -> Result<(), String> {
match pending.kind {
PendingImportKind::ForwardDecl => {
if let Some(decl) = self.remapping.find_decl(pending.source_decl_id) {
if let Some(name) = decl.name() {
if let Some(chain) = self.redecl_chains.get(name) {
if chain.is_complete {
return Ok(());
}
}
}
}
Err("forward declaration still unresolved".into())
}
PendingImportKind::UnresolvedTemplate => {
Err("template arguments still unresolved".into())
}
PendingImportKind::ODRCandidate => Err("ODR candidate still pending comparison".into()),
PendingImportKind::TypeDependency => Err("type dependency still unresolved".into()),
}
}
fn finalize_chains(&mut self, errors: &mut Vec<String>) {
for (name, chain) in &self.redecl_chains {
if !chain.is_consistent {
errors.push(format!("inconsistent redeclaration chain for '{}'", name));
}
}
}
pub fn get_unified_decls(&self) -> Vec<Decl> {
self.redecl_chains
.values()
.map(|chain| chain.canonical.clone())
.collect()
}
pub fn import_report(&self) -> String {
let mut report = String::new();
report.push_str(&format!(
"AST Import Report\n\
=================\n\
Declarations imported: {}\n\
ODR violations: {}\n\
Redecl chains: {}\n\
Template specs: {}\n\
Diagnostics: {}\n",
self.import_count,
self.odr_violations,
self.redecl_chains.len(),
self.template_merger.specialization_count(),
self.diagnostics.len(),
));
for diag in &self.diagnostics {
report.push_str(&format!(
" [{}] {}\n",
match diag.level {
ImportDiagLevel::Note => "note",
ImportDiagLevel::Warning => "warning",
ImportDiagLevel::Error => "error",
ImportDiagLevel::Fatal => "fatal",
},
diag.message
));
}
report
}
}
pub const AST_SERIALIZATION_VERSION: u32 = 28;
pub const AST_FILE_MAGIC: [u8; 4] = *b"CPCH";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum ASTBlockID {
ModuleBlock = 1,
MetadataBlock = 2,
IdentifierBlock = 3,
TypeBlock = 4,
DeclBlock = 5,
StmtBlock = 6,
SourceLocationBlock = 7,
PreprocessorBlock = 8,
ControlBlock = 9,
TemplateBlock = 10,
}
pub struct ASTWriter {
buffer: Vec<u8>,
pos: usize,
type_ids: HashMap<String, TypeID>,
next_type_id: TypeID,
next_decl_id: DeclID,
next_ident_id: IdentID,
file_path: PathBuf,
sloc_offsets: Vec<(u64, SourceLocation)>,
lazy_mode: bool,
}
impl ASTWriter {
pub fn new(file_path: &str, lazy_mode: bool) -> Self {
Self {
buffer: Vec::with_capacity(64 * 1024),
pos: 0,
type_ids: HashMap::new(),
next_type_id: 1,
next_decl_id: 1,
next_ident_id: 1,
file_path: PathBuf::from(file_path),
sloc_offsets: Vec::new(),
lazy_mode,
}
}
pub fn write_tu(&mut self, tu: &TranslationUnit) -> io::Result<Vec<u8>> {
self.buffer.clear();
self.pos = 0;
self.write_magic()?;
self.write_metadata_block(tu)?;
self.write_identifier_block(tu)?;
self.write_type_block(tu)?;
self.write_decl_block(tu)?;
self.write_sloc_block(tu)?;
self.write_control_block(tu)?;
Ok(self.buffer.clone())
}
pub fn write_to_file(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let data = self.write_tu(tu)?;
let mut file = std::fs::File::create(&self.file_path)?;
file.write_all(&data)?;
Ok(())
}
fn write_magic(&mut self) -> io::Result<()> {
self.write_bytes(&AST_FILE_MAGIC)?;
self.write_u32(AST_SERIALIZATION_VERSION)?;
Ok(())
}
fn write_metadata_block(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let block_id = ASTBlockID::MetadataBlock as u32;
self.write_u32(block_id)?;
let size_pos = self.buffer.len();
self.write_u32(0)?;
let triple = b"x86_64-unknown-linux-gnu";
self.write_u32(triple.len() as u32)?;
self.write_bytes(triple)?;
let path_lossy = tu.file_path.to_string_lossy();
let path_bytes = path_lossy.as_bytes();
self.write_u32(path_bytes.len() as u32)?;
self.write_bytes(path_bytes)?;
let end_pos = self.buffer.len();
let block_size = (end_pos - size_pos - 4) as u32;
self.patch_u32(size_pos, block_size)?;
Ok(())
}
fn write_identifier_block(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let block_id = ASTBlockID::IdentifierBlock as u32;
self.write_u32(block_id)?;
let size_pos = self.buffer.len();
self.write_u32(0)?;
let identifiers = self.collect_identifiers(tu);
self.write_u32(identifiers.len() as u32)?;
for ident in &identifiers {
let id = self.next_ident_id;
self.next_ident_id += 1;
self.write_u64(id)?;
let bytes = ident.as_bytes();
self.write_u32(bytes.len() as u32)?;
self.write_bytes(bytes)?;
}
let end_pos = self.buffer.len();
self.patch_u32(size_pos, (end_pos - size_pos - 4) as u32)?;
Ok(())
}
fn write_type_block(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let block_id = ASTBlockID::TypeBlock as u32;
self.write_u32(block_id)?;
let size_pos = self.buffer.len();
self.write_u32(0)?;
let types = self.collect_types(tu);
self.write_u32(types.len() as u32)?;
for ty in &types {
let fp = type_fingerprint(ty);
let type_id = if let Some(&existing) = self.type_ids.get(&fp) {
existing
} else {
let id = self.next_type_id;
self.next_type_id += 1;
self.type_ids.insert(fp, id);
id
};
self.write_u64(type_id)?;
self.write_type(ty)?;
}
let end_pos = self.buffer.len();
self.patch_u32(size_pos, (end_pos - size_pos - 4) as u32)?;
Ok(())
}
fn write_decl_block(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let block_id = ASTBlockID::DeclBlock as u32;
self.write_u32(block_id)?;
let size_pos = self.buffer.len();
self.write_u32(0)?;
self.write_u32(tu.declarations.len() as u32)?;
for decl in &tu.declarations {
let decl_id = self.next_decl_id;
self.next_decl_id += 1;
self.write_u64(decl_id)?;
self.write_decl(decl)?;
}
let end_pos = self.buffer.len();
self.patch_u32(size_pos, (end_pos - size_pos - 4) as u32)?;
Ok(())
}
fn write_sloc_block(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let block_id = ASTBlockID::SourceLocationBlock as u32;
self.write_u32(block_id)?;
let size_pos = self.buffer.len();
self.write_u32(0)?;
let offsets: Vec<(u64, SourceLocation)> = self
.sloc_offsets
.iter()
.map(|(k, v)| (*k, v.clone()))
.collect();
self.write_u32(offsets.len() as u32)?;
for (offset, loc) in &offsets {
self.write_u64(*offset)?;
self.write_u32(loc.file_id as u32)?;
self.write_u32(loc.line)?;
self.write_u32(loc.column)?;
self.write_u64(loc.offset)?;
}
let end_pos = self.buffer.len();
self.patch_u32(size_pos, (end_pos - size_pos - 4) as u32)?;
Ok(())
}
fn write_control_block(&mut self, tu: &TranslationUnit) -> io::Result<()> {
let block_id = ASTBlockID::ControlBlock as u32;
self.write_u32(block_id)?;
let size_pos = self.buffer.len();
self.write_u32(0)?;
self.write_u32(1)?;
let path_lossy = tu.file_path.to_string_lossy();
let path_bytes = path_lossy.as_bytes();
self.write_u32(path_bytes.len() as u32)?;
self.write_bytes(path_bytes)?;
let end_pos = self.buffer.len();
self.patch_u32(size_pos, (end_pos - size_pos - 4) as u32)?;
Ok(())
}
fn write_type(&mut self, ty: &QualType) -> io::Result<()> {
let qual_bits: u8 = (if ty.is_const { 1 } else { 0 })
| (if ty.is_volatile { 2 } else { 0 })
| (if ty.is_restrict { 4 } else { 0 });
self.write_u8(qual_bits)?;
self.write_type_node(&ty.base)?;
Ok(())
}
fn write_type_node(&mut self, ty: &TypeNode) -> io::Result<()> {
match ty {
TypeNode::Void => self.write_u8(0),
TypeNode::Char => self.write_u8(1),
TypeNode::SChar => self.write_u8(2),
TypeNode::UChar => self.write_u8(3),
TypeNode::Short => self.write_u8(4),
TypeNode::UShort => self.write_u8(5),
TypeNode::Int => self.write_u8(6),
TypeNode::UInt => self.write_u8(7),
TypeNode::Long => self.write_u8(8),
TypeNode::ULong => self.write_u8(9),
TypeNode::LongLong => self.write_u8(10),
TypeNode::ULongLong => self.write_u8(11),
TypeNode::Float => self.write_u8(12),
TypeNode::Double => self.write_u8(13),
TypeNode::LongDouble => self.write_u8(14),
TypeNode::Bool => self.write_u8(15),
TypeNode::Complex => self.write_u8(16),
TypeNode::Auto => self.write_u8(17),
TypeNode::Pointer(inner) => {
self.write_u8(18)?;
self.write_type(inner)?;
Ok(())
}
TypeNode::Array { elem, size } => {
self.write_u8(19)?;
let has_size = size.is_some();
self.write_u8(if has_size { 1 } else { 0 })?;
if let Some(sz) = size {
self.write_u64(*sz as u64)?;
}
self.write_type(elem)?;
Ok(())
}
TypeNode::Function {
ret,
params,
is_vararg,
} => {
self.write_u8(20)?;
self.write_type(ret)?;
self.write_u32(params.len() as u32)?;
for p in params {
self.write_type(p)?;
}
self.write_u8(if *is_vararg { 1 } else { 0 })?;
Ok(())
}
TypeNode::Struct {
name,
fields,
is_union,
} => {
self.write_u8(if *is_union { 22 } else { 21 })?;
self.write_opt_string(name.as_deref())?;
self.write_u32(fields.len() as u32)?;
for f in fields {
self.write_string(&f.name)?;
self.write_type(&f.ty)?;
match f.bit_width {
Some(w) => {
self.write_u8(1)?;
self.write_u32(w)?;
}
None => self.write_u8(0)?,
}
}
Ok(())
}
TypeNode::Enum { name, variants } => {
self.write_u8(23)?;
self.write_opt_string(name.as_deref())?;
self.write_u32(variants.len() as u32)?;
for v in variants {
self.write_string(&v.name)?;
match v.value {
Some(val) => {
self.write_u8(1)?;
self.write_i64(val)?;
}
None => self.write_u8(0)?,
}
}
Ok(())
}
TypeNode::Typedef { name, underlying } => {
self.write_u8(24)?;
self.write_string(name)?;
self.write_type(underlying)?;
Ok(())
}
TypeNode::Record(name) => {
self.write_u8(25)?;
self.write_string(name)?;
Ok(())
}
}
}
fn write_decl(&mut self, decl: &Decl) -> io::Result<()> {
match decl {
Decl::Function(f) => {
self.write_u8(0)?; self.write_string(&f.name)?;
self.write_type(&f.ret_ty)?;
self.write_u32(f.params.len() as u32)?;
for p in &f.params {
self.write_string(&p.name)?;
self.write_type(&p.ty)?;
}
self.write_u8(if f.is_vararg { 1 } else { 0 })?;
self.write_u8(if f.is_inline { 1 } else { 0 })?;
self.write_u8(if f.is_noreturn { 1 } else { 0 })?;
match &f.body {
Some(body) => {
self.write_u8(1)?;
self.write_compound_stmt(body)?;
}
None => self.write_u8(0)?,
}
Ok(())
}
Decl::Variable(v) => {
self.write_u8(1)?; self.write_string(&v.name)?;
self.write_type(&v.ty)?;
match &v.init {
Some(init) => {
self.write_u8(1)?;
self.write_expr(init)?;
}
None => self.write_u8(0)?,
}
self.write_u8(if v.is_extern { 1 } else { 0 })?;
self.write_u8(if v.is_static { 1 } else { 0 })?;
self.write_u8(if v.is_global { 1 } else { 0 })?;
Ok(())
}
Decl::Typedef(t) => {
self.write_u8(2)?;
self.write_string(&t.name)?;
self.write_type(&t.underlying)?;
Ok(())
}
Decl::Struct(s) => {
self.write_u8(3)?;
self.write_opt_string(s.name.as_deref())?;
self.write_u32(s.fields.len() as u32)?;
for f in &s.fields {
self.write_string(&f.name)?;
self.write_type(&f.ty)?;
match f.bit_width {
Some(w) => {
self.write_u8(1)?;
self.write_u32(w)?;
}
None => self.write_u8(0)?,
}
}
self.write_u8(if s.is_union { 1 } else { 0 })?;
Ok(())
}
Decl::Enum(e) => {
self.write_u8(4)?;
self.write_opt_string(e.name.as_deref())?;
self.write_type(&e.underlying)?;
self.write_u32(e.variants.len() as u32)?;
for v in &e.variants {
self.write_string(&v.name)?;
match v.value {
Some(val) => {
self.write_u8(1)?;
self.write_i64(val)?;
}
None => self.write_u8(0)?,
}
}
Ok(())
}
Decl::EnumVariant(ev) => {
self.write_u8(5)?;
self.write_string(&ev.name)?;
self.write_opt_string(ev.enum_name.as_deref())?;
match ev.value {
Some(val) => {
self.write_u8(1)?;
self.write_i64(val)?;
}
None => self.write_u8(0)?,
}
Ok(())
}
}
}
fn write_expr(&mut self, expr: &Expr) -> io::Result<()> {
match expr {
Expr::IntLiteral(v) => {
self.write_u8(0)?;
self.write_i64(*v)?;
}
Expr::UIntLiteral(v, is_ll) => {
self.write_u8(1)?;
self.write_u64(*v)?;
self.write_u8(if *is_ll { 1 } else { 0 })?;
}
Expr::FloatLiteral(v) => {
self.write_u8(2)?;
self.write_f64(*v)?;
}
Expr::DoubleLiteral(v) => {
self.write_u8(3)?;
self.write_f64(*v)?;
}
Expr::CharLiteral(c) => {
self.write_u8(4)?;
self.write_u32(*c as u32)?;
}
Expr::StringLiteral(s) => {
self.write_u8(5)?;
self.write_string(s)?;
}
Expr::Ident(name) => {
self.write_u8(6)?;
self.write_string(name)?;
}
Expr::Unary(op, inner) => {
self.write_u8(7)?;
self.write_unary_op(op)?;
self.write_expr(inner)?;
}
Expr::Binary(op, lhs, rhs) => {
self.write_u8(8)?;
self.write_binary_op(op)?;
self.write_expr(lhs)?;
self.write_expr(rhs)?;
}
Expr::Assign(op, lhs, rhs) => {
self.write_u8(9)?;
self.write_binary_op(op)?;
self.write_expr(lhs)?;
self.write_expr(rhs)?;
}
Expr::Cast(ty, inner) => {
self.write_u8(10)?;
self.write_type(ty)?;
self.write_expr(inner)?;
}
Expr::Conditional(cond, then, els) => {
self.write_u8(11)?;
self.write_expr(cond)?;
self.write_expr(then)?;
self.write_expr(els)?;
}
Expr::Call { callee, args } => {
self.write_u8(12)?;
self.write_expr(callee)?;
self.write_u32(args.len() as u32)?;
for arg in args {
self.write_expr(arg)?;
}
}
Expr::Subscript { base, index } => {
self.write_u8(13)?;
self.write_expr(base)?;
self.write_expr(index)?;
}
Expr::Member {
base,
field,
is_arrow,
} => {
self.write_u8(14)?;
self.write_expr(base)?;
self.write_string(field)?;
self.write_u8(if *is_arrow { 1 } else { 0 })?;
}
Expr::SizeOf(inner) => {
self.write_u8(15)?;
self.write_expr(inner)?;
}
Expr::SizeOfType(ty) => {
self.write_u8(16)?;
self.write_type(ty)?;
}
Expr::AlignOf(inner) => {
self.write_u8(17)?;
self.write_expr(inner)?;
}
Expr::AlignOfType(ty) => {
self.write_u8(18)?;
self.write_type(ty)?;
}
Expr::PostInc(inner) => {
self.write_u8(19)?;
self.write_expr(inner)?;
}
Expr::PostDec(inner) => {
self.write_u8(20)?;
self.write_expr(inner)?;
}
Expr::PreInc(inner) => {
self.write_u8(21)?;
self.write_expr(inner)?;
}
Expr::PreDec(inner) => {
self.write_u8(22)?;
self.write_expr(inner)?;
}
Expr::CompoundLiteral(ty, body) => {
self.write_u8(23)?;
self.write_type(ty)?;
self.write_expr(body)?;
}
Expr::AggregateLiteral(vals) => {
self.write_u8(24)?;
self.write_u32(vals.len() as u32)?;
for v in vals {
self.write_expr(v)?;
}
}
}
Ok(())
}
fn write_compound_stmt(&mut self, stmt: &CompoundStmt) -> io::Result<()> {
self.write_u32(stmt.stmts.len() as u32)?;
for s in &stmt.stmts {
self.write_stmt(s)?;
}
Ok(())
}
fn write_stmt(&mut self, stmt: &Stmt) -> io::Result<()> {
match stmt {
Stmt::Compound(c) => {
self.write_u8(0)?;
self.write_compound_stmt(c)?;
}
Stmt::Return(expr) => {
self.write_u8(1)?;
match expr {
Some(e) => {
self.write_u8(1)?;
self.write_expr(e)?;
}
None => self.write_u8(0)?,
}
}
Stmt::If { cond, then, els } => {
self.write_u8(2)?;
self.write_expr(cond)?;
self.write_stmt(then)?;
match els {
Some(e) => {
self.write_u8(1)?;
self.write_stmt(e)?;
}
None => self.write_u8(0)?,
}
}
Stmt::While { cond, body } => {
self.write_u8(3)?;
self.write_expr(cond)?;
self.write_stmt(body)?;
}
Stmt::DoWhile { body, cond } => {
self.write_u8(4)?;
self.write_stmt(body)?;
self.write_expr(cond)?;
}
Stmt::For {
init,
cond,
incr,
body,
} => {
self.write_u8(5)?;
match init {
Some(i) => {
self.write_u8(1)?;
self.write_stmt(i)?;
}
None => self.write_u8(0)?,
}
match cond {
Some(c) => {
self.write_u8(1)?;
self.write_expr(c)?;
}
None => self.write_u8(0)?,
}
match incr {
Some(i) => {
self.write_u8(1)?;
self.write_expr(i)?;
}
None => self.write_u8(0)?,
}
self.write_stmt(body)?;
}
Stmt::Switch { expr, body } => {
self.write_u8(6)?;
self.write_expr(expr)?;
self.write_stmt(body)?;
}
Stmt::Case { value, stmt } => {
self.write_u8(7)?;
self.write_expr(value)?;
self.write_stmt(stmt)?;
}
Stmt::Default { stmt } => {
self.write_u8(8)?;
self.write_stmt(stmt)?;
}
Stmt::Break => self.write_u8(9)?,
Stmt::Continue => self.write_u8(10)?,
Stmt::Goto { label } => {
self.write_u8(11)?;
self.write_string(label)?;
}
Stmt::Label { name, stmt } => {
self.write_u8(12)?;
self.write_string(name)?;
self.write_stmt(stmt)?;
}
Stmt::Expr(e) => {
self.write_u8(13)?;
self.write_expr(e)?;
}
Stmt::Decl(d) => {
self.write_u8(14)?;
self.write_string(&d.name)?;
self.write_type(&d.ty)?;
match &d.init {
Some(init) => {
self.write_u8(1)?;
self.write_expr(init)?;
}
None => self.write_u8(0)?,
}
}
Stmt::Null => self.write_u8(15)?,
}
Ok(())
}
fn write_unary_op(&mut self, op: &UnaryOp) -> io::Result<()> {
let v: u8 = match op {
UnaryOp::Plus => 0,
UnaryOp::Minus => 1,
UnaryOp::Not => 2,
UnaryOp::BitNot => 3,
UnaryOp::Deref => 4,
UnaryOp::AddrOf => 5,
};
self.write_u8(v)
}
fn write_binary_op(&mut self, op: &BinaryOp) -> io::Result<()> {
let v: u8 = match op {
BinaryOp::Add => 0,
BinaryOp::Sub => 1,
BinaryOp::Mul => 2,
BinaryOp::Div => 3,
BinaryOp::Mod => 4,
BinaryOp::And => 5,
BinaryOp::Or => 6,
BinaryOp::Xor => 7,
BinaryOp::Shl => 8,
BinaryOp::Shr => 9,
BinaryOp::Eq => 10,
BinaryOp::Ne => 11,
BinaryOp::Lt => 12,
BinaryOp::Gt => 13,
BinaryOp::Le => 14,
BinaryOp::Ge => 15,
BinaryOp::LogicAnd => 16,
BinaryOp::LogicOr => 17,
BinaryOp::Assign => 18,
_ => 255,
};
self.write_u8(v)
}
fn collect_identifiers(&self, tu: &TranslationUnit) -> Vec<String> {
let mut idents = HashSet::new();
for decl in &tu.declarations {
if let Some(name) = decl.name() {
idents.insert(name.to_string());
}
match decl {
Decl::Function(f) => {
for p in &f.params {
idents.insert(p.name.clone());
}
}
Decl::Struct(s) => {
for f in &s.fields {
idents.insert(f.name.clone());
}
}
Decl::Enum(e) => {
for v in &e.variants {
idents.insert(v.name.clone());
}
}
_ => {}
}
}
let mut result: Vec<String> = idents.into_iter().collect();
result.sort();
result
}
fn collect_types(&self, tu: &TranslationUnit) -> Vec<QualType> {
let mut seen = HashSet::new();
let mut types: Vec<QualType> = Vec::new();
for decl in &tu.declarations {
match decl {
Decl::Function(f) => {
let fp = type_fingerprint(&f.ret_ty);
if seen.insert(fp.clone()) {
types.push(f.ret_ty.clone());
}
for p in &f.params {
let fp = type_fingerprint(&p.ty);
if seen.insert(fp) {
types.push(p.ty.clone());
}
}
}
Decl::Variable(v) => {
let fp = type_fingerprint(&v.ty);
if seen.insert(fp) {
types.push(v.ty.clone());
}
}
Decl::Typedef(t) => {
let fp = type_fingerprint(&t.underlying);
if seen.insert(fp) {
types.push(t.underlying.clone());
}
}
Decl::Struct(s) => {
for f in &s.fields {
let fp = type_fingerprint(&f.ty);
if seen.insert(fp) {
types.push(f.ty.clone());
}
}
}
Decl::Enum(e) => {
let fp = type_fingerprint(&e.underlying);
if seen.insert(fp) {
types.push(e.underlying.clone());
}
}
_ => {}
}
}
types
}
fn write_u8(&mut self, v: u8) -> io::Result<()> {
self.buffer.push(v);
self.pos += 1;
Ok(())
}
fn write_u32(&mut self, v: u32) -> io::Result<()> {
self.buffer.extend_from_slice(&v.to_le_bytes());
self.pos += 4;
Ok(())
}
fn write_u64(&mut self, v: u64) -> io::Result<()> {
self.buffer.extend_from_slice(&v.to_le_bytes());
self.pos += 8;
Ok(())
}
fn write_i64(&mut self, v: i64) -> io::Result<()> {
self.write_u64(v as u64)
}
fn write_f64(&mut self, v: f64) -> io::Result<()> {
self.buffer.extend_from_slice(&v.to_le_bytes());
self.pos += 8;
Ok(())
}
fn write_bytes(&mut self, bytes: &[u8]) -> io::Result<()> {
self.buffer.extend_from_slice(bytes);
self.pos += bytes.len();
Ok(())
}
fn write_string(&mut self, s: &str) -> io::Result<()> {
let bytes = s.as_bytes();
self.write_u32(bytes.len() as u32)?;
self.write_bytes(bytes)
}
fn write_opt_string(&mut self, s: Option<&str>) -> io::Result<()> {
match s {
Some(text) => {
self.write_u8(1)?;
self.write_string(text)?;
}
None => {
self.write_u8(0)?;
}
}
Ok(())
}
fn patch_u32(&mut self, pos: usize, val: u32) -> io::Result<()> {
if pos + 4 <= self.buffer.len() {
self.buffer[pos..pos + 4].copy_from_slice(&val.to_le_bytes());
}
Ok(())
}
}
pub struct ASTReader {
buffer: Vec<u8>,
pos: usize,
types: BTreeMap<TypeID, QualType>,
decls: BTreeMap<DeclID, Decl>,
identifiers: BTreeMap<IdentID, String>,
sloc_table: BTreeMap<u64, SourceLocation>,
metadata: ASTFileMetadata,
lazy_mode: bool,
lazy_decl_queue: Vec<DeclID>,
decl_offsets: BTreeMap<DeclID, u64>,
}
#[derive(Debug, Clone, Default)]
pub struct ASTFileMetadata {
pub version: u32,
pub target_triple: String,
pub file_path: String,
pub decl_count: u32,
pub type_count: u32,
pub ident_count: u32,
}
impl ASTReader {
pub fn new(data: Vec<u8>, lazy_mode: bool) -> Self {
Self {
buffer: data,
pos: 0,
types: BTreeMap::new(),
decls: BTreeMap::new(),
identifiers: BTreeMap::new(),
sloc_table: BTreeMap::new(),
metadata: ASTFileMetadata::default(),
lazy_mode,
lazy_decl_queue: Vec::new(),
decl_offsets: BTreeMap::new(),
}
}
pub fn from_file(path: &Path, lazy_mode: bool) -> io::Result<Self> {
let data = std::fs::read(path)?;
Ok(Self::new(data, lazy_mode))
}
pub fn read_header(&mut self) -> io::Result<()> {
let mut magic = [0u8; 4];
self.read_exact(&mut magic)?;
if magic != AST_FILE_MAGIC {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid AST file magic",
));
}
let version = self.read_u32()?;
self.metadata.version = version;
if version != AST_SERIALIZATION_VERSION {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"AST file version mismatch: expected {}, got {}",
AST_SERIALIZATION_VERSION, version
),
));
}
Ok(())
}
pub fn read_all(&mut self) -> io::Result<TranslationUnit> {
self.read_header()?;
let mut tu = TranslationUnit::new("<deserialized>");
while self.pos < self.buffer.len() {
let block_id_raw = self.read_u32()?;
let block_size = self.read_u32()? as usize;
let block_start = self.pos;
let block_end = block_start + block_size;
let block_id = match block_id_raw {
1 => ASTBlockID::ModuleBlock,
2 => ASTBlockID::MetadataBlock,
3 => ASTBlockID::IdentifierBlock,
4 => ASTBlockID::TypeBlock,
5 => ASTBlockID::DeclBlock,
6 => ASTBlockID::StmtBlock,
7 => ASTBlockID::SourceLocationBlock,
8 => ASTBlockID::PreprocessorBlock,
9 => ASTBlockID::ControlBlock,
10 => ASTBlockID::TemplateBlock,
_ => {
self.pos = block_end;
continue;
}
};
match block_id {
ASTBlockID::MetadataBlock => self.read_metadata_block()?,
ASTBlockID::IdentifierBlock => self.read_identifier_block()?,
ASTBlockID::TypeBlock => self.read_type_block()?,
ASTBlockID::DeclBlock => self.read_decl_block(&mut tu)?,
ASTBlockID::SourceLocationBlock => self.read_sloc_block()?,
ASTBlockID::ControlBlock => self.read_control_block()?,
_ => {
}
}
self.pos = block_end;
}
if self.lazy_mode {
self.resolve_lazy_decls(&mut tu)?;
}
if !self.metadata.file_path.is_empty() {
tu.file_path = PathBuf::from(&self.metadata.file_path);
}
Ok(tu)
}
fn read_metadata_block(&mut self) -> io::Result<()> {
let triple_len = self.read_u32()? as usize;
let mut triple = vec![0u8; triple_len];
self.read_exact(&mut triple)?;
self.metadata.target_triple = String::from_utf8_lossy(&triple).to_string();
let path_len = self.read_u32()? as usize;
let mut path = vec![0u8; path_len];
self.read_exact(&mut path)?;
self.metadata.file_path = String::from_utf8_lossy(&path).to_string();
Ok(())
}
fn read_identifier_block(&mut self) -> io::Result<()> {
let count = self.read_u32()? as usize;
for _ in 0..count {
let id = self.read_u64()?;
let len = self.read_u32()? as usize;
let mut bytes = vec![0u8; len];
self.read_exact(&mut bytes)?;
let s = String::from_utf8_lossy(&bytes).to_string();
self.identifiers.insert(id, s);
}
self.metadata.ident_count = count as u32;
Ok(())
}
fn read_type_block(&mut self) -> io::Result<()> {
let count = self.read_u32()? as usize;
for _ in 0..count {
let type_id = self.read_u64()?;
if self.lazy_mode {
self.types.insert(type_id, QualType::void());
continue;
}
let ty = self.read_type()?;
self.types.insert(type_id, ty);
}
self.metadata.type_count = count as u32;
Ok(())
}
fn read_decl_block(&mut self, tu: &mut TranslationUnit) -> io::Result<()> {
let count = self.read_u32()? as usize;
for _ in 0..count {
let decl_id = self.read_u64()?;
if self.lazy_mode {
self.decl_offsets.insert(decl_id, self.pos as u64);
self.lazy_decl_queue.push(decl_id);
self.skip_decl()?;
} else {
let decl = self.read_decl()?;
self.decls.insert(decl_id, decl.clone());
tu.declarations.push(decl);
}
}
self.metadata.decl_count = count as u32;
Ok(())
}
fn read_sloc_block(&mut self) -> io::Result<()> {
let count = self.read_u32()? as usize;
for _ in 0..count {
let offset = self.read_u64()?;
let loc = SourceLocation {
file_id: self.read_u32()? as u64,
line: self.read_u32()?,
column: self.read_u32()?,
offset: self.read_u64()?,
};
self.sloc_table.insert(offset, loc);
}
Ok(())
}
fn read_control_block(&mut self) -> io::Result<()> {
let file_count = self.read_u32()?;
for _ in 0..file_count {
let path_len = self.read_u32()? as usize;
let mut path = vec![0u8; path_len];
self.read_exact(&mut path)?;
let _file_path = String::from_utf8_lossy(&path).to_string();
}
Ok(())
}
fn read_type(&mut self) -> io::Result<QualType> {
let qual_bits = self.read_u8()?;
let is_const = (qual_bits & 1) != 0;
let is_volatile = (qual_bits & 2) != 0;
let is_restrict = (qual_bits & 4) != 0;
let base = self.read_type_node()?;
Ok(QualType {
base: Box::new(base),
is_const,
is_volatile,
is_restrict,
})
}
fn read_type_node(&mut self) -> io::Result<TypeNode> {
let tag = self.read_u8()?;
match tag {
0 => Ok(TypeNode::Void),
1 => Ok(TypeNode::Char),
2 => Ok(TypeNode::SChar),
3 => Ok(TypeNode::UChar),
4 => Ok(TypeNode::Short),
5 => Ok(TypeNode::UShort),
6 => Ok(TypeNode::Int),
7 => Ok(TypeNode::UInt),
8 => Ok(TypeNode::Long),
9 => Ok(TypeNode::ULong),
10 => Ok(TypeNode::LongLong),
11 => Ok(TypeNode::ULongLong),
12 => Ok(TypeNode::Float),
13 => Ok(TypeNode::Double),
14 => Ok(TypeNode::LongDouble),
15 => Ok(TypeNode::Bool),
16 => Ok(TypeNode::Complex),
17 => Ok(TypeNode::Auto),
18 => {
let inner = self.read_type()?;
Ok(TypeNode::Pointer(Box::new(inner)))
}
19 => {
let has_size = self.read_u8()? != 0;
let size = if has_size {
Some(self.read_u64()? as usize)
} else {
None
};
let elem = self.read_type()?;
Ok(TypeNode::Array {
elem: Box::new(elem),
size,
})
}
20 => {
let ret = self.read_type()?;
let param_count = self.read_u32()? as usize;
let mut params = Vec::with_capacity(param_count);
for _ in 0..param_count {
params.push(self.read_type()?);
}
let is_vararg = self.read_u8()? != 0;
Ok(TypeNode::Function {
ret: Box::new(ret),
params,
is_vararg,
})
}
21 => {
let name = self.read_opt_string()?;
let field_count = self.read_u32()? as usize;
let mut fields = Vec::with_capacity(field_count);
for _ in 0..field_count {
let fname = self.read_string()?;
let fty = self.read_type()?;
let bit_width = if self.read_u8()? != 0 {
Some(self.read_u32()?)
} else {
None
};
fields.push(FieldDecl {
name: fname,
ty: fty,
bit_width,
});
}
Ok(TypeNode::Struct {
name,
fields,
is_union: false,
})
}
22 => {
let name = self.read_opt_string()?;
let field_count = self.read_u32()? as usize;
let mut fields = Vec::with_capacity(field_count);
for _ in 0..field_count {
let fname = self.read_string()?;
let fty = self.read_type()?;
let bit_width = if self.read_u8()? != 0 {
Some(self.read_u32()?)
} else {
None
};
fields.push(FieldDecl {
name: fname,
ty: fty,
bit_width,
});
}
Ok(TypeNode::Struct {
name,
fields,
is_union: true,
})
}
23 => {
let name = self.read_opt_string()?;
let variant_count = self.read_u32()? as usize;
let mut variants = Vec::with_capacity(variant_count);
for _ in 0..variant_count {
let vname = self.read_string()?;
let value = if self.read_u8()? != 0 {
Some(self.read_i64()?)
} else {
None
};
variants.push(EnumVariant { name: vname, value });
}
Ok(TypeNode::Enum { name, variants })
}
24 => {
let name = self.read_string()?;
let underlying = self.read_type()?;
Ok(TypeNode::Typedef {
name,
underlying: Box::new(underlying),
})
}
25 => {
let name = self.read_string()?;
Ok(TypeNode::Record(name))
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown type tag: {}", tag),
)),
}
}
fn read_decl(&mut self) -> io::Result<Decl> {
let tag = self.read_u8()?;
match tag {
0 => {
let name = self.read_string()?;
let ret_ty = self.read_type()?;
let param_count = self.read_u32()? as usize;
let mut params = Vec::with_capacity(param_count);
for _ in 0..param_count {
let pname = self.read_string()?;
let pty = self.read_type()?;
params.push(VarDecl::new(&pname, pty));
}
let is_vararg = self.read_u8()? != 0;
let is_inline = self.read_u8()? != 0;
let is_noreturn = self.read_u8()? != 0;
let has_body = self.read_u8()? != 0;
let body = if has_body {
Some(self.read_compound_stmt()?)
} else {
None
};
Ok(Decl::Function(FunctionDecl {
name,
ret_ty,
params,
body,
is_vararg,
linkage: Linkage::External,
is_inline,
is_noreturn,
}))
}
1 => {
let name = self.read_string()?;
let ty = self.read_type()?;
let has_init = self.read_u8()? != 0;
let init = if has_init {
Some(Box::new(self.read_expr()?))
} else {
None
};
let is_extern = self.read_u8()? != 0;
let is_static = self.read_u8()? != 0;
let is_global = self.read_u8()? != 0;
Ok(Decl::Variable(VarDecl {
name,
ty,
init,
linkage: if is_static {
Linkage::Internal
} else {
Linkage::External
},
is_global,
is_extern,
is_static,
}))
}
2 => {
let name = self.read_string()?;
let underlying = self.read_type()?;
Ok(Decl::Typedef(TypedefDecl { name, underlying }))
}
3 => {
let name = self.read_opt_string()?;
let field_count = self.read_u32()? as usize;
let mut fields = Vec::with_capacity(field_count);
for _ in 0..field_count {
let fname = self.read_string()?;
let fty = self.read_type()?;
let bit_width = if self.read_u8()? != 0 {
Some(self.read_u32()?)
} else {
None
};
fields.push(FieldDecl {
name: fname,
ty: fty,
bit_width,
});
}
let is_union = self.read_u8()? != 0;
Ok(Decl::Struct(StructDecl {
name,
fields,
is_union,
}))
}
4 => {
let name = self.read_opt_string()?;
let underlying = self.read_type()?;
let variant_count = self.read_u32()? as usize;
let mut variants = Vec::with_capacity(variant_count);
for _ in 0..variant_count {
let vname = self.read_string()?;
let value = if self.read_u8()? != 0 {
Some(self.read_i64()?)
} else {
None
};
variants.push(EnumVariant { name: vname, value });
}
Ok(Decl::Enum(EnumDecl {
name,
variants,
underlying,
}))
}
5 => {
let name = self.read_string()?;
let enum_name = self.read_opt_string()?;
let value = if self.read_u8()? != 0 {
Some(self.read_i64()?)
} else {
None
};
Ok(Decl::EnumVariant(EnumVariantDecl {
name,
value,
enum_name,
}))
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown decl tag: {}", tag),
)),
}
}
fn read_expr(&mut self) -> io::Result<Expr> {
let tag = self.read_u8()?;
match tag {
0 => Ok(Expr::IntLiteral(self.read_i64()?)),
1 => {
let v = self.read_u64()?;
let is_ll = self.read_u8()? != 0;
Ok(Expr::UIntLiteral(v, is_ll))
}
2 => Ok(Expr::FloatLiteral(self.read_f64()?)),
3 => Ok(Expr::DoubleLiteral(self.read_f64()?)),
4 => {
let c = self.read_u32()?;
Ok(Expr::CharLiteral(char::from_u32(c).unwrap_or('?')))
}
5 => Ok(Expr::StringLiteral(self.read_string()?)),
6 => Ok(Expr::Ident(self.read_string()?)),
7 => {
let op = self.read_unary_op()?;
let inner = self.read_expr()?;
Ok(Expr::Unary(op, Box::new(inner)))
}
8 => {
let op = self.read_binary_op()?;
let lhs = self.read_expr()?;
let rhs = self.read_expr()?;
Ok(Expr::Binary(op, Box::new(lhs), Box::new(rhs)))
}
9 => {
let op = self.read_binary_op()?;
let lhs = self.read_expr()?;
let rhs = self.read_expr()?;
Ok(Expr::Assign(op, Box::new(lhs), Box::new(rhs)))
}
10 => {
let ty = self.read_type()?;
let inner = self.read_expr()?;
Ok(Expr::Cast(ty, Box::new(inner)))
}
11 => {
let cond = self.read_expr()?;
let then = self.read_expr()?;
let els = self.read_expr()?;
Ok(Expr::Conditional(
Box::new(cond),
Box::new(then),
Box::new(els),
))
}
12 => {
let callee = self.read_expr()?;
let arg_count = self.read_u32()? as usize;
let mut args = Vec::with_capacity(arg_count);
for _ in 0..arg_count {
args.push(self.read_expr()?);
}
Ok(Expr::Call {
callee: Box::new(callee),
args,
})
}
13 => {
let base = self.read_expr()?;
let index = self.read_expr()?;
Ok(Expr::Subscript {
base: Box::new(base),
index: Box::new(index),
})
}
14 => {
let base = self.read_expr()?;
let field = self.read_string()?;
let is_arrow = self.read_u8()? != 0;
Ok(Expr::Member {
base: Box::new(base),
field,
is_arrow,
})
}
15 => Ok(Expr::SizeOf(Box::new(self.read_expr()?))),
16 => Ok(Expr::SizeOfType(self.read_type()?)),
17 => Ok(Expr::AlignOf(Box::new(self.read_expr()?))),
18 => Ok(Expr::AlignOfType(self.read_type()?)),
19 => Ok(Expr::PostInc(Box::new(self.read_expr()?))),
20 => Ok(Expr::PostDec(Box::new(self.read_expr()?))),
21 => Ok(Expr::PreInc(Box::new(self.read_expr()?))),
22 => Ok(Expr::PreDec(Box::new(self.read_expr()?))),
23 => {
let ty = self.read_type()?;
let body = self.read_expr()?;
Ok(Expr::CompoundLiteral(ty, Box::new(body)))
}
24 => {
let len = self.read_u32()? as usize;
let mut vals = Vec::with_capacity(len);
for _ in 0..len {
vals.push(self.read_expr()?);
}
Ok(Expr::AggregateLiteral(vals))
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown expr tag: {}", tag),
)),
}
}
fn read_stmt(&mut self) -> io::Result<Stmt> {
let tag = self.read_u8()?;
match tag {
0 => Ok(Stmt::Compound(self.read_compound_stmt()?)),
1 => {
let has_expr = self.read_u8()? != 0;
let expr = if has_expr {
Some(Box::new(self.read_expr()?))
} else {
None
};
Ok(Stmt::Return(expr))
}
2 => {
let cond = self.read_expr()?;
let then = self.read_stmt()?;
let has_else = self.read_u8()? != 0;
let els = if has_else {
Some(Box::new(self.read_stmt()?))
} else {
None
};
Ok(Stmt::If {
cond: Box::new(cond),
then: Box::new(then),
els,
})
}
3 => {
let cond = self.read_expr()?;
let body = self.read_stmt()?;
Ok(Stmt::While {
cond: Box::new(cond),
body: Box::new(body),
})
}
4 => {
let body = self.read_stmt()?;
let cond = self.read_expr()?;
Ok(Stmt::DoWhile {
body: Box::new(body),
cond: Box::new(cond),
})
}
5 => {
let has_init = self.read_u8()? != 0;
let init = if has_init {
Some(Box::new(self.read_stmt()?))
} else {
None
};
let has_cond = self.read_u8()? != 0;
let cond = if has_cond {
Some(Box::new(self.read_expr()?))
} else {
None
};
let has_incr = self.read_u8()? != 0;
let incr = if has_incr {
Some(Box::new(self.read_expr()?))
} else {
None
};
let body = self.read_stmt()?;
Ok(Stmt::For {
init,
cond,
incr,
body: Box::new(body),
})
}
6 => {
let expr = self.read_expr()?;
let body = self.read_stmt()?;
Ok(Stmt::Switch {
expr: Box::new(expr),
body: Box::new(body),
})
}
7 => {
let value = self.read_expr()?;
let stmt = self.read_stmt()?;
Ok(Stmt::Case {
value: Box::new(value),
stmt: Box::new(stmt),
})
}
8 => Ok(Stmt::Default {
stmt: Box::new(self.read_stmt()?),
}),
9 => Ok(Stmt::Break),
10 => Ok(Stmt::Continue),
11 => Ok(Stmt::Goto {
label: self.read_string()?,
}),
12 => {
let name = self.read_string()?;
let stmt = self.read_stmt()?;
Ok(Stmt::Label {
name,
stmt: Box::new(stmt),
})
}
13 => Ok(Stmt::Expr(Box::new(self.read_expr()?))),
14 => {
let name = self.read_string()?;
let ty = self.read_type()?;
let has_init = self.read_u8()? != 0;
let init = if has_init {
Some(Box::new(self.read_expr()?))
} else {
None
};
Ok(Stmt::Decl(Box::new(VarDecl {
name,
ty,
init,
linkage: Linkage::None,
is_global: false,
is_extern: false,
is_static: false,
})))
}
15 => Ok(Stmt::Null),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown stmt tag: {}", tag),
)),
}
}
fn read_compound_stmt(&mut self) -> io::Result<CompoundStmt> {
let count = self.read_u32()? as usize;
let mut stmts = Vec::with_capacity(count);
for _ in 0..count {
stmts.push(self.read_stmt()?);
}
Ok(CompoundStmt { stmts })
}
fn read_unary_op(&mut self) -> io::Result<UnaryOp> {
match self.read_u8()? {
0 => Ok(UnaryOp::Plus),
1 => Ok(UnaryOp::Minus),
2 => Ok(UnaryOp::Not),
3 => Ok(UnaryOp::BitNot),
4 => Ok(UnaryOp::Deref),
5 => Ok(UnaryOp::AddrOf),
v => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown unary op: {}", v),
)),
}
}
fn read_binary_op(&mut self) -> io::Result<BinaryOp> {
match self.read_u8()? {
0 => Ok(BinaryOp::Add),
1 => Ok(BinaryOp::Sub),
2 => Ok(BinaryOp::Mul),
3 => Ok(BinaryOp::Div),
4 => Ok(BinaryOp::Mod),
5 => Ok(BinaryOp::And),
6 => Ok(BinaryOp::Or),
7 => Ok(BinaryOp::Xor),
8 => Ok(BinaryOp::Shl),
9 => Ok(BinaryOp::Shr),
10 => Ok(BinaryOp::Eq),
11 => Ok(BinaryOp::Ne),
12 => Ok(BinaryOp::Lt),
13 => Ok(BinaryOp::Gt),
14 => Ok(BinaryOp::Le),
15 => Ok(BinaryOp::Ge),
16 => Ok(BinaryOp::LogicAnd),
17 => Ok(BinaryOp::LogicOr),
18 => Ok(BinaryOp::Assign),
v => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown binary op: {}", v),
)),
}
}
fn skip_decl(&mut self) -> io::Result<()> {
let tag = self.read_u8()?;
match tag {
0 => self.skip_function_decl()?,
1 => self.skip_variable_decl()?,
2 => self.skip_typedef_decl()?,
3 => self.skip_struct_decl()?,
4 => self.skip_enum_decl()?,
5 => self.skip_enum_variant_decl()?,
_ => {}
}
Ok(())
}
fn skip_function_decl(&mut self) -> io::Result<()> {
let _name = self.read_string()?;
let _ret = self.read_type()?;
let param_count = self.read_u32()?;
for _ in 0..param_count {
let _pname = self.read_string()?;
let _pty = self.read_type()?;
}
let _vararg = self.read_u8()?;
let _inline = self.read_u8()?;
let _noreturn = self.read_u8()?;
let has_body = self.read_u8()? != 0;
if has_body {
let _body = self.read_compound_stmt()?;
}
Ok(())
}
fn skip_variable_decl(&mut self) -> io::Result<()> {
let _name = self.read_string()?;
let _ty = self.read_type()?;
let has_init = self.read_u8()? != 0;
if has_init {
let _init = self.read_expr()?;
}
let _extern = self.read_u8()?;
let _static = self.read_u8()?;
let _global = self.read_u8()?;
Ok(())
}
fn skip_typedef_decl(&mut self) -> io::Result<()> {
let _name = self.read_string()?;
let _underlying = self.read_type()?;
Ok(())
}
fn skip_struct_decl(&mut self) -> io::Result<()> {
let _name = self.read_opt_string()?;
let field_count = self.read_u32()? as usize;
for _ in 0..field_count {
let _fname = self.read_string()?;
let _fty = self.read_type()?;
if self.read_u8()? != 0 {
let _bw = self.read_u32()?;
}
}
let _is_union = self.read_u8()?;
Ok(())
}
fn skip_enum_decl(&mut self) -> io::Result<()> {
let _name = self.read_opt_string()?;
let _underlying = self.read_type()?;
let variant_count = self.read_u32()? as usize;
for _ in 0..variant_count {
let _vname = self.read_string()?;
if self.read_u8()? != 0 {
let _val = self.read_i64()?;
}
}
Ok(())
}
fn skip_enum_variant_decl(&mut self) -> io::Result<()> {
let _name = self.read_string()?;
let _enum_name = self.read_opt_string()?;
if self.read_u8()? != 0 {
let _val = self.read_i64()?;
}
Ok(())
}
fn resolve_lazy_decls(&mut self, tu: &mut TranslationUnit) -> io::Result<()> {
let saved_pos = self.pos;
let decls_to_resolve: Vec<(u64, u64)> = self
.lazy_decl_queue
.iter()
.filter_map(|&decl_id| self.decl_offsets.get(&decl_id).map(|&off| (decl_id, off)))
.collect();
for (decl_id, offset) in decls_to_resolve {
self.pos = offset as usize;
match self.read_decl() {
Ok(decl) => {
self.decls.insert(decl_id, decl.clone());
tu.declarations.push(decl);
}
Err(e) => {
return Err(e);
}
}
}
self.pos = saved_pos;
Ok(())
}
fn read_u8(&mut self) -> io::Result<u8> {
if self.pos >= self.buffer.len() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected end of AST data",
));
}
let v = self.buffer[self.pos];
self.pos += 1;
Ok(v)
}
fn read_u32(&mut self) -> io::Result<u32> {
let mut buf = [0u8; 4];
self.read_exact(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
fn read_u64(&mut self) -> io::Result<u64> {
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
fn read_i64(&mut self) -> io::Result<i64> {
self.read_u64().map(|v| v as i64)
}
fn read_f64(&mut self) -> io::Result<f64> {
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
Ok(f64::from_le_bytes(buf))
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
let end = self.pos + buf.len();
if end > self.buffer.len() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected end of AST data",
));
}
buf.copy_from_slice(&self.buffer[self.pos..end]);
self.pos = end;
Ok(())
}
fn read_string(&mut self) -> io::Result<String> {
let len = self.read_u32()? as usize;
let mut buf = vec![0u8; len];
self.read_exact(&mut buf)?;
Ok(String::from_utf8_lossy(&buf).to_string())
}
fn read_opt_string(&mut self) -> io::Result<Option<String>> {
let has = self.read_u8()? != 0;
if has {
Ok(Some(self.read_string()?))
} else {
Ok(None)
}
}
pub fn get_decl(&self, id: DeclID) -> Option<&Decl> {
self.decls.get(&id)
}
pub fn get_type(&self, id: TypeID) -> Option<&QualType> {
self.types.get(&id)
}
pub fn get_ident(&self, id: IdentID) -> Option<&str> {
self.identifiers.get(&id).map(|s| s.as_str())
}
pub fn translate_sloc(&self, offset: u64) -> Option<&SourceLocation> {
self.sloc_table.get(&offset)
}
}
pub struct SourceLocationTranslator {
file_map: HashMap<u64, u64>,
line_offsets: HashMap<u64, i64>,
column_offsets: HashMap<u64, i64>,
}
impl SourceLocationTranslator {
pub fn new() -> Self {
Self {
file_map: HashMap::new(),
line_offsets: HashMap::new(),
column_offsets: HashMap::new(),
}
}
pub fn map_file(&mut self, source_file_id: u64, dest_file_id: u64) {
self.file_map.insert(source_file_id, dest_file_id);
}
pub fn set_line_offset(&mut self, dest_file_id: u64, offset: i64) {
self.line_offsets.insert(dest_file_id, offset);
}
pub fn set_column_offset(&mut self, dest_file_id: u64, offset: i64) {
self.column_offsets.insert(dest_file_id, offset);
}
pub fn translate(&self, loc: &SourceLocation) -> SourceLocation {
let file_id = self
.file_map
.get(&loc.file_id)
.copied()
.unwrap_or(loc.file_id);
let line_offset = self.line_offsets.get(&file_id).copied().unwrap_or(0);
let col_offset = self.column_offsets.get(&file_id).copied().unwrap_or(0);
SourceLocation {
file_id,
line: ((loc.line as i64) + line_offset).max(1) as u32,
column: ((loc.column as i64) + col_offset).max(1) as u32,
offset: loc.offset,
}
}
}
use super::ast::Linkage;
pub fn import_translation_unit(
source: &TranslationUnit,
dest: &mut TranslationUnit,
standard: CLangStandard,
) -> Result<(), Vec<String>> {
let mut importer = X86ASTImport::new(standard);
let result = importer.import_tu(source);
if result.is_ok() {
std::mem::swap(dest, &mut importer.dest_tu);
}
result
}
pub fn serialize_tu_to_file(tu: &TranslationUnit, path: &str) -> io::Result<()> {
let mut writer = ASTWriter::new(path, false);
writer.write_to_file(tu)
}
pub fn deserialize_tu_from_file(path: &str) -> io::Result<TranslationUnit> {
let mut reader = ASTReader::from_file(Path::new(path), false)?;
reader.read_all()
}
pub fn check_odr_equivalence(a: &Decl, b: &Decl, standard: CLangStandard) -> DeclRelationship {
let mut comparator = DeclComparator::new(standard);
comparator.compare(a, b)
}
pub fn build_redecl_chain(
name: &str,
decls: Vec<Decl>,
standard: CLangStandard,
) -> Result<RedeclChain, String> {
let mut comparator = DeclComparator::new(standard);
let mut chain = RedeclChain::new(decls[0].clone());
for decl in decls.iter().skip(1) {
let dummy_loc = SourceLocation {
file_id: 0,
line: 0,
column: 0,
offset: 0,
};
chain.add_decl(decl.clone(), dummy_loc, &mut comparator)?;
}
Ok(chain)
}
#[cfg(test)]
mod tests {
use super::*;
fn c11_std() -> CLangStandard {
CLangStandard::C11
}
fn make_int_type() -> QualType {
QualType::int()
}
fn make_void_type() -> QualType {
QualType::void()
}
#[test]
fn test_type_fingerprint_primitive() {
let t1 = QualType::int();
let t2 = QualType::int();
assert_eq!(type_fingerprint(&t1), type_fingerprint(&t2));
let t3 = QualType::const_(TypeNode::Int);
assert_ne!(type_fingerprint(&t1), type_fingerprint(&t3));
}
#[test]
fn test_type_fingerprint_pointer() {
let t1 = QualType::pointer_to(QualType::int());
let t2 = QualType::pointer_to(QualType::int());
assert_eq!(type_fingerprint(&t1), type_fingerprint(&t2));
let t3 = QualType::pointer_to(QualType::const_(TypeNode::Int));
assert_ne!(type_fingerprint(&t1), type_fingerprint(&t3));
}
#[test]
fn test_structural_equiv_simple() {
let mut checker = StructuralEquivChecker::new(false);
let t1 = QualType::int();
let t2 = QualType::int();
assert_eq!(
checker.are_equivalent(&t1, &t2),
EquivalenceResult::Equivalent
);
let t3 = QualType::float();
assert!(matches!(
checker.are_equivalent(&t1, &t3),
EquivalenceResult::NotEquivalent(_)
));
}
#[test]
fn test_structural_equiv_const() {
let mut checker = StructuralEquivChecker::new(false);
let t1 = QualType::const_(TypeNode::Int);
let t2 = QualType::int();
assert!(matches!(
checker.are_equivalent(&t1, &t2),
EquivalenceResult::NotEquivalent(_)
));
}
#[test]
fn test_structural_equiv_odr_mode() {
let mut checker = StructuralEquivChecker::new(true);
let t1 = QualType::const_(TypeNode::Int);
let t2 = QualType::int();
assert_eq!(
checker.are_equivalent(&t1, &t2),
EquivalenceResult::Equivalent
);
let t3 = QualType::int();
let t4 = QualType::const_(TypeNode::Int);
assert_eq!(
checker.are_equivalent(&t3, &t4),
EquivalenceResult::Equivalent
);
}
#[test]
fn test_decl_comparator_function_redecl() {
let mut comparator = DeclComparator::new(c11_std());
let fwd = Decl::Function(FunctionDecl {
name: "foo".into(),
ret_ty: make_int_type(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
let def = Decl::Function(FunctionDecl {
name: "foo".into(),
ret_ty: make_int_type(),
params: vec![],
body: Some(CompoundStmt::new()),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
assert_eq!(
comparator.compare(&fwd, &def),
DeclRelationship::Redeclaration
);
}
#[test]
fn test_decl_comparator_odr_conflict() {
let mut comparator = DeclComparator::new(c11_std());
let def1 = Decl::Function(FunctionDecl {
name: "bar".into(),
ret_ty: make_int_type(),
params: vec![],
body: Some(CompoundStmt::new()),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
let def2 = Decl::Function(FunctionDecl {
name: "bar".into(),
ret_ty: make_void_type(),
params: vec![],
body: Some(CompoundStmt::new()),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
assert!(matches!(
comparator.compare(&def1, &def2),
DeclRelationship::ODRConflict(_)
));
}
#[test]
fn test_import_simple() {
let tu = TranslationUnit {
file_path: PathBuf::from("test.c"),
declarations: vec![Decl::Function(FunctionDecl {
name: "hello".into(),
ret_ty: make_int_type(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
source: String::new(),
};
let mut importer = X86ASTImport::new(c11_std());
let result = importer.import_tu(&tu);
assert!(result.is_ok());
assert_eq!(importer.import_count, 1);
}
#[test]
fn test_serialization_roundtrip() {
let tu = TranslationUnit {
file_path: PathBuf::from("roundtrip.c"),
declarations: vec![
Decl::Function(FunctionDecl {
name: "add".into(),
ret_ty: make_int_type(),
params: vec![
VarDecl::new("a", make_int_type()),
VarDecl::new("b", make_int_type()),
],
body: Some(CompoundStmt::new()),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
}),
Decl::Variable(VarDecl {
name: "global_x".into(),
ty: make_int_type(),
init: Some(Box::new(Expr::IntLiteral(42))),
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
}),
],
source: String::new(),
};
let mut writer = ASTWriter::new("_roundtrip_test.ast", false);
let data = writer.write_tu(&tu).expect("write");
let mut reader = ASTReader::new(data, false);
let restored = reader.read_all().expect("read");
assert_eq!(restored.declarations.len(), tu.declarations.len());
assert_eq!(restored.declarations[0].name(), tu.declarations[0].name());
assert_eq!(restored.declarations[1].name(), tu.declarations[1].name());
}
#[test]
fn test_redecl_chain_function() {
let fwd = Decl::Function(FunctionDecl {
name: "baz".into(),
ret_ty: make_int_type(),
params: vec![VarDecl::new("x", make_int_type())],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
let def = Decl::Function(FunctionDecl {
name: "baz".into(),
ret_ty: make_int_type(),
params: vec![VarDecl::new("x", make_int_type())],
body: Some(CompoundStmt::new()),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
let mut chain = RedeclChain::new(fwd.clone());
let mut comparator = DeclComparator::new(c11_std());
let dummy_loc = SourceLocation {
file_id: 0,
line: 0,
column: 0,
offset: 0,
};
chain
.add_decl(def.clone(), dummy_loc.clone(), &mut comparator)
.expect("add def");
assert!(chain.is_complete);
assert!(chain.is_consistent);
if let Decl::Function(f) = &chain.canonical {
assert!(f.body.is_some());
} else {
panic!("expected function decl");
}
}
#[test]
fn test_template_specialization_merge() {
let mut merger = TemplateSpecializationMerger::new(c11_std());
let decl_a = Decl::Function(FunctionDecl {
name: "max".into(),
ret_ty: make_int_type(),
params: vec![
VarDecl::new("a", make_int_type()),
VarDecl::new("b", make_int_type()),
],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
});
let spec = TemplateSpecialization::new(
"max",
vec!["int".into(), "int".into()],
decl_a,
true,
false,
);
let result = merger.register(spec);
assert!(result.is_ok());
assert!(result.unwrap());
assert_eq!(merger.specialization_count(), 1);
}
#[test]
fn test_source_location_translation() {
let mut translator = SourceLocationTranslator::new();
translator.map_file(1, 10);
translator.set_line_offset(10, 5);
translator.set_column_offset(10, 3);
let src = SourceLocation {
file_id: 1,
line: 20,
column: 10,
offset: 100,
};
let dst = translator.translate(&src);
assert_eq!(dst.file_id, 10);
assert_eq!(dst.line, 25);
assert_eq!(dst.column, 13);
assert_eq!(dst.offset, 100);
}
#[test]
fn test_serialization_lazy_mode() {
let tu = TranslationUnit {
file_path: PathBuf::from("lazy.c"),
declarations: vec![Decl::Function(FunctionDecl {
name: "lazy_func".into(),
ret_ty: make_int_type(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
source: String::new(),
};
let mut writer = ASTWriter::new("_lazy_test.ast", true);
let data = writer.write_tu(&tu).expect("write");
let mut reader = ASTReader::new(data, true);
let restored = reader.read_all().expect("read");
assert_eq!(restored.declarations.len(), 1);
assert_eq!(restored.declarations[0].name(), Some("lazy_func"));
}
}