use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::io::{self, BufReader, BufWriter, Cursor, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::clang::ast::*;
use crate::clang::clang_modules_full_x86::{
X86BMITransfer, X86ModuleCache, X86ModuleCompiler, X86ModuleDependency, X86ModuleImport,
X86ModuleLinkage, X86ModuleMap, X86ModuleOwnership, X86ModuleStandardLibrary, X86Modules,
X86_BMI_EXTENDED_VERSION, X86_BMI_EXTENSION, X86_BMI_SIGNATURE_MAGIC, X86_CACHE_MAX_SIZE_BYTES,
X86_CACHE_MAX_VERSIONS, X86_MODULE_CACHE_DIR, X86_MODULE_HASH_ALGORITHM, X86_MODULE_MAP_FILE,
};
use crate::clang::clang_serialization_x86::{
ASTRecordType, X86ASTContext, X86ASTWriter, X86ModuleFile, X86SourceManagerExtensions,
};
use crate::clang::cpp_modules::{
BmiFile, GlobalModuleFragment, HeaderUnitSynthesizer, ModuleDecl, ModuleDependencyGraph,
ModuleImport, ModuleKind, ModuleMap, ModuleMapFile, ModuleName, ModuleOwnership,
ModuleValidator, PrivateModuleFragment,
};
use crate::clang::lexer::{Lexer, SourceLocation, SourceManager};
use crate::clang::preprocessor::{MacroDefinition, Preprocessor, PreprocessorState};
use crate::clang::token::{Token, TokenKind};
use crate::clang::{CLangStandard, ClangOptions};
use crate::x86::{
x86_calling_convention::X86CallingConvention, x86_frame_lowering::X86FrameLowering,
x86_target_machine::X86TargetMachine, X86_STACK_ALIGNMENT_64,
};
pub const X86_PCH_MAGIC: &[u8; 4] = b"CPCH";
pub const X86_PCH_VERSION_MAJOR: u32 = 17;
pub const X86_PCH_VERSION_MINOR: u32 = 0;
pub const X86_PCH_EXTENSION: &str = ".pch";
pub const X86_PCH_STAT_CACHE_EXTENSION: &str = ".pch.stat";
pub const X86_MODULE_BMI_SUBDIR: &str = "bmi";
pub const X86_MODULE_PCH_SUBDIR: &str = "pch";
pub const X86_PCH_MAX_CHAIN_DEPTH: u32 = 16;
pub const X86_MAX_IDENTIFIERS: usize = 1_000_000;
pub const X86_MAX_SOURCE_FILES: usize = 100_000;
pub const X86_STAT_CACHE_TTL_SECS: u64 = 60;
pub const X86_CONTENT_HASH_ALGORITHM: &str = "blake3-256";
pub const X86_BMI_BLOCK_AST: u32 = 0x41535420;
pub const X86_BMI_BLOCK_IDENTIFIER: u32 = 0x4944454e;
pub const X86_BMI_BLOCK_SOURCE_MANAGER: u32 = 0x5352434d;
pub const X86_BMI_BLOCK_PREPROCESSOR: u32 = 0x5050524f;
pub const X86_BMI_BLOCK_CONTROL: u32 = 0x4354524c;
pub const X86_BMI_BLOCK_INPUT_FILES: u32 = 0x494e5055;
pub const X86_BMI_BLOCK_MODULE_MAP: u32 = 0x4d4f4455;
pub const X86_BMI_BLOCK_MODULE_DEPENDENCIES: u32 = 0x4d444550;
pub const X86_PCH_BLOCK_AST: u32 = X86_BMI_BLOCK_AST;
pub const X86_PCH_BLOCK_IDENTIFIER: u32 = X86_BMI_BLOCK_IDENTIFIER;
pub const X86_PCH_BLOCK_SOURCE_MANAGER: u32 = X86_BMI_BLOCK_SOURCE_MANAGER;
pub const X86_PCH_BLOCK_PREPROCESSOR: u32 = X86_BMI_BLOCK_PREPROCESSOR;
pub const X86_PCH_BLOCK_CONTROL: u32 = X86_BMI_BLOCK_CONTROL;
pub const X86_PCH_BLOCK_INPUT_FILES: u32 = X86_BMI_BLOCK_INPUT_FILES;
pub const X86_PCH_BLOCK_CHAIN_METADATA: u32 = 0x43484149;
pub struct X86PCHModules {
pub pch_generator: X86PCHGenerator,
pub pch_validator: X86PCHValidator,
pub pch_loader: X86PCHLoader,
pub pch_stat_cache: X86PCHStatCache,
pub pch_chainer: X86PCHChaining,
pub module_interface_compiler: X86ModuleInterfaceCompiler,
pub module_impl_compiler: X86ModuleImplCompiler,
pub bmi_serializer: X86BMISerializer,
pub module_map_parser: X86ModuleMapParser,
pub module_cache: X86ModuleCacheManager,
pub dependency_scanner: X86ModuleDependencyScanner,
pub visibility_manager: X86ModuleVisibilityManager,
pub header_modules: X86HeaderModules,
pub framework_modules: X86FrameworkModules,
pub module_map_language: X86ModuleMapLanguage,
pub pch_bmi_format: X86PCHBMIFormat,
pub input_validator: X86InputFileValidator,
pub target_machine: X86TargetMachine,
pub options: ClangOptions,
pub pch_enabled: bool,
pub modules_enabled: bool,
pub framework_modules_enabled: bool,
pub header_modules_enabled: bool,
pub pch_chain: Vec<X86PCHFile>,
pub loaded_modules: HashMap<ModuleName, X86ModuleInterface>,
pub pending_queue: VecDeque<X86CompilationRequest>,
pub stats: X86PCHModuleStats,
}
#[derive(Debug, Clone)]
pub struct X86PCHFile {
pub path: PathBuf,
pub header: X86PCHHeader,
pub validated: bool,
pub chain_index: u32,
pub kind: X86PCHKind,
pub dependencies: Vec<PathBuf>,
pub content_hash: [u8; 32],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86PCHKind {
System,
Project,
Module,
PrefixHeader,
}
impl fmt::Display for X86PCHKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::System => write!(f, "system"),
Self::Project => write!(f, "project"),
Self::Module => write!(f, "module"),
Self::PrefixHeader => write!(f, "prefix-header"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86PCHHeader {
pub magic: [u8; 4],
pub version_major: u32,
pub version_minor: u32,
pub target_triple: String,
pub language_standard: CLangStandard,
pub is_cxx: bool,
pub file_size: u64,
pub timestamp: SystemTime,
pub content_hash: [u8; 32],
pub num_identifiers: u32,
pub num_source_files: u32,
pub num_macros: u32,
pub num_declarations: u32,
pub block_offsets: HashMap<u32, u64>,
}
#[derive(Debug, Clone)]
pub struct X86CompilationRequest {
pub source_path: PathBuf,
pub output_path: PathBuf,
pub kind: X86CompilationRequestKind,
pub dependencies: Vec<ModuleName>,
pub options: ClangOptions,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CompilationRequestKind {
GeneratePCH,
UsePCH,
GenerateModuleInterface,
GenerateModuleImplementation,
UseModule,
ScanDependencies,
}
#[derive(Debug, Clone, Default)]
pub struct X86PCHModuleStats {
pub pch_generated: u64,
pub pch_loaded: u64,
pub pch_validations: u64,
pub pch_validation_failures: u64,
pub stat_cache_hits: u64,
pub stat_cache_misses: u64,
pub pch_chained: u64,
pub module_interfaces_compiled: u64,
pub module_implementations_compiled: u64,
pub bmi_written: u64,
pub bmi_read: u64,
pub module_maps_parsed: u64,
pub module_cache_hits: u64,
pub module_cache_misses: u64,
pub dependency_scans: u64,
pub header_modules_processed: u64,
pub framework_modules_processed: u64,
pub total_bytes_written: u64,
pub total_bytes_read: u64,
}
impl X86PCHModules {
pub fn new(target_machine: X86TargetMachine, options: ClangOptions) -> Self {
Self {
pch_generator: X86PCHGenerator::new(),
pch_validator: X86PCHValidator::new(),
pch_loader: X86PCHLoader::new(),
pch_stat_cache: X86PCHStatCache::new(),
pch_chainer: X86PCHChaining::new(),
module_interface_compiler: X86ModuleInterfaceCompiler::new(),
module_impl_compiler: X86ModuleImplCompiler::new(),
bmi_serializer: X86BMISerializer::new(),
module_map_parser: X86ModuleMapParser::new(),
module_cache: X86ModuleCacheManager::new(),
dependency_scanner: X86ModuleDependencyScanner::new(),
visibility_manager: X86ModuleVisibilityManager::new(),
header_modules: X86HeaderModules::new(),
framework_modules: X86FrameworkModules::new(),
module_map_language: X86ModuleMapLanguage::new(),
pch_bmi_format: X86PCHBMIFormat::new(),
input_validator: X86InputFileValidator::new(),
target_machine,
options,
pch_enabled: true,
modules_enabled: true,
framework_modules_enabled: false,
header_modules_enabled: true,
pch_chain: Vec::new(),
loaded_modules: HashMap::new(),
pending_queue: VecDeque::new(),
stats: X86PCHModuleStats::default(),
}
}
pub fn set_pch_enabled(&mut self, enabled: bool) {
self.pch_enabled = enabled;
}
pub fn set_modules_enabled(&mut self, enabled: bool) {
self.modules_enabled = enabled;
}
pub fn set_framework_modules_enabled(&mut self, enabled: bool) {
self.framework_modules_enabled = enabled;
}
pub fn set_header_modules_enabled(&mut self, enabled: bool) {
self.header_modules_enabled = enabled;
}
pub fn generate_pch(
&mut self,
header_path: &Path,
output_path: &Path,
options: &ClangOptions,
) -> Result<X86PCHFile, X86PCHModulesError> {
if !self.pch_enabled {
return Err(X86PCHModulesError::PCHDisabled);
}
let pch_file = self
.pch_generator
.generate(header_path, output_path, options)?;
self.stats.pch_generated += 1;
self.stats.total_bytes_written += pch_file.header.file_size;
Ok(pch_file)
}
pub fn validate_pch(
&mut self,
pch_path: &Path,
options: &ClangOptions,
) -> Result<X86PCHValidationResult, X86PCHModulesError> {
self.stats.pch_validations += 1;
let result = self
.pch_validator
.validate(pch_path, options, &mut self.pch_stat_cache)?;
if !result.is_valid {
self.stats.pch_validation_failures += 1;
}
Ok(result)
}
pub fn load_pch(&mut self, pch_path: &Path) -> Result<X86PCHLoadResult, X86PCHModulesError> {
let result = self.pch_loader.load(pch_path, &mut self.pch_stat_cache)?;
self.stats.pch_loaded += 1;
self.stats.total_bytes_read += result.bytes_read;
Ok(result)
}
pub fn chain_pch_files(
&mut self,
pch_files: &[PathBuf],
options: &ClangOptions,
) -> Result<Vec<X86PCHFile>, X86PCHModulesError> {
if pch_files.len() > X86_PCH_MAX_CHAIN_DEPTH as usize {
return Err(X86PCHModulesError::ChainDepthExceeded);
}
let chain = self
.pch_chainer
.chain(pch_files, options, &mut self.pch_stat_cache)?;
self.stats.pch_chained += chain.len() as u64;
self.pch_chain = chain.clone();
Ok(chain)
}
pub fn compile_module_interface(
&mut self,
source_path: &Path,
module_name: &ModuleName,
output_path: &Path,
options: &ClangOptions,
) -> Result<X86ModuleInterface, X86PCHModulesError> {
if !self.modules_enabled {
return Err(X86PCHModulesError::ModulesDisabled);
}
let interface = self.module_interface_compiler.compile(
source_path,
module_name,
output_path,
options,
)?;
self.stats.module_interfaces_compiled += 1;
self.loaded_modules
.insert(module_name.clone(), interface.clone());
Ok(interface)
}
pub fn compile_module_implementation(
&mut self,
source_path: &Path,
module_name: &ModuleName,
output_path: &Path,
options: &ClangOptions,
) -> Result<X86ModuleImplementation, X86PCHModulesError> {
if !self.modules_enabled {
return Err(X86PCHModulesError::ModulesDisabled);
}
let implementation =
self.module_impl_compiler
.compile(source_path, module_name, output_path, options)?;
self.stats.module_implementations_compiled += 1;
Ok(implementation)
}
pub fn write_bmi(
&mut self,
interface: &X86ModuleInterface,
output_path: &Path,
) -> Result<(), X86PCHModulesError> {
self.bmi_serializer.serialize(interface, output_path)?;
self.stats.bmi_written += 1;
Ok(())
}
pub fn read_bmi(&mut self, bmi_path: &Path) -> Result<X86ModuleInterface, X86PCHModulesError> {
let interface = self.bmi_serializer.deserialize(bmi_path)?;
self.stats.bmi_read += 1;
Ok(interface)
}
pub fn parse_module_map(
&mut self,
map_path: &Path,
) -> Result<X86ParsedModuleMap, X86PCHModulesError> {
let map = self.module_map_parser.parse(map_path)?;
self.stats.module_maps_parsed += 1;
Ok(map)
}
pub fn check_module_cache(&mut self, module_name: &ModuleName) -> Option<PathBuf> {
self.module_cache.lookup(module_name)
}
pub fn store_module_cache(
&mut self,
module_name: &ModuleName,
bmi_path: &Path,
) -> Result<(), X86PCHModulesError> {
self.module_cache.store(module_name, bmi_path)
}
pub fn scan_module_dependencies(
&mut self,
source_path: &Path,
) -> Result<X86DependencyScanResult, X86PCHModulesError> {
let result = self.dependency_scanner.scan(source_path)?;
self.stats.dependency_scans += 1;
Ok(result)
}
pub fn set_module_visibility(
&mut self,
module_name: &ModuleName,
visibility: X86ModuleVisibility,
) {
self.visibility_manager
.set_visibility(module_name, visibility);
}
pub fn process_header_module(
&mut self,
header_path: &Path,
) -> Result<X86HeaderModule, X86PCHModulesError> {
let module = self.header_modules.process(header_path)?;
self.stats.header_modules_processed += 1;
Ok(module)
}
pub fn process_framework_module(
&mut self,
framework_path: &Path,
) -> Result<X86FrameworkModule, X86PCHModulesError> {
let module = self.framework_modules.process(framework_path)?;
self.stats.framework_modules_processed += 1;
Ok(module)
}
pub fn get_stats(&self) -> &X86PCHModuleStats {
&self.stats
}
}
#[derive(Debug)]
pub enum X86PCHModulesError {
IoError(io::Error),
PCHDisabled,
ModulesDisabled,
InvalidPCHFormat(String),
PCHValidationFailed(String),
ModuleNotFound(ModuleName),
ModuleCompilationFailed(String),
BMISerializationFailed(String),
ModuleMapParseError(String),
StatCacheError(String),
ChainDepthExceeded,
ChainingError(String),
DependencyError(String),
CacheError(String),
UnsupportedLanguageOption(String),
UnsupportedTarget(String),
HeaderSearchPathMismatch(String),
InputHashMismatch(String),
FrameworkModuleNotFound(PathBuf),
DuplicateModuleName(ModuleName),
}
impl fmt::Display for X86PCHModulesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IoError(e) => write!(f, "I/O error: {}", e),
Self::PCHDisabled => write!(f, "PCH support is disabled"),
Self::ModulesDisabled => write!(f, "Modules support is disabled"),
Self::InvalidPCHFormat(msg) => write!(f, "Invalid PCH format: {}", msg),
Self::PCHValidationFailed(msg) => write!(f, "PCH validation failed: {}", msg),
Self::ModuleNotFound(name) => write!(f, "Module not found: {}", name),
Self::ModuleCompilationFailed(msg) => write!(f, "Module compilation failed: {}", msg),
Self::BMISerializationFailed(msg) => write!(f, "BMI serialization failed: {}", msg),
Self::ModuleMapParseError(msg) => write!(f, "Module map parse error: {}", msg),
Self::StatCacheError(msg) => write!(f, "Stat cache error: {}", msg),
Self::ChainDepthExceeded => write!(
f,
"PCH chain depth exceeded maximum of {}",
X86_PCH_MAX_CHAIN_DEPTH
),
Self::ChainingError(msg) => write!(f, "PCH chaining error: {}", msg),
Self::DependencyError(msg) => write!(f, "Dependency error: {}", msg),
Self::CacheError(msg) => write!(f, "Cache error: {}", msg),
Self::UnsupportedLanguageOption(msg) => {
write!(f, "Unsupported language option: {}", msg)
}
Self::UnsupportedTarget(msg) => write!(f, "Unsupported target: {}", msg),
Self::HeaderSearchPathMismatch(msg) => {
write!(f, "Header search path mismatch: {}", msg)
}
Self::InputHashMismatch(msg) => write!(f, "Input file hash mismatch: {}", msg),
Self::FrameworkModuleNotFound(p) => {
write!(f, "Framework module not found: {}", p.display())
}
Self::DuplicateModuleName(name) => write!(f, "Duplicate module name: {}", name),
}
}
}
impl From<io::Error> for X86PCHModulesError {
fn from(e: io::Error) -> Self {
Self::IoError(e)
}
}
pub struct X86PCHGenerator {
pub ast_writer: X86ASTWriter,
pub include_identifiers: bool,
pub include_macros: bool,
pub include_source_manager: bool,
pub debug_log: bool,
pub self_validate: bool,
buffer: Vec<u8>,
identifier_table: HashMap<String, u32>,
macro_definitions: HashMap<String, MacroDefinitionData>,
source_files: Vec<X86SourceFileEntry>,
block_offsets: HashMap<u32, u64>,
}
#[derive(Debug, Clone)]
pub struct MacroDefinitionData {
pub name: String,
pub body: String,
pub is_function_like: bool,
pub parameters: Vec<String>,
pub is_builtin: bool,
pub is_variadic: bool,
pub source_location: SourceLocation,
pub is_defined: bool,
}
#[derive(Debug, Clone)]
pub struct X86SourceFileEntry {
pub path: PathBuf,
pub size: u64,
pub mtime: SystemTime,
pub content_hash: [u8; 32],
pub is_system: bool,
pub is_extern_c: bool,
pub line_table: Vec<X86LineEntry>,
}
#[derive(Debug, Clone)]
pub struct X86LineEntry {
pub file_offset: u64,
pub line: u32,
pub column: u32,
pub is_line_directive: bool,
}
#[derive(Debug, Clone)]
pub struct X86PCHLoadResult {
pub ast: Option<TranslationUnit>,
pub identifiers: HashMap<String, u32>,
pub macros: HashMap<String, MacroDefinitionData>,
pub source_files: Vec<X86SourceFileEntry>,
pub preprocessor_state: Option<PreprocessorState>,
pub bytes_read: u64,
pub success: bool,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86PCHValidationResult {
pub is_valid: bool,
pub failure_reason: Option<String>,
pub language_options_match: bool,
pub target_options_match: bool,
pub header_search_paths_match: bool,
pub stat_cache_used: bool,
pub duration_ms: u64,
}
impl X86PCHGenerator {
pub fn new() -> Self {
Self {
ast_writer: X86ASTWriter::new(),
include_identifiers: true,
include_macros: true,
include_source_manager: true,
debug_log: false,
self_validate: false,
buffer: Vec::with_capacity(16 * 1024 * 1024), identifier_table: HashMap::new(),
macro_definitions: HashMap::new(),
source_files: Vec::new(),
block_offsets: HashMap::new(),
}
}
pub fn generate(
&mut self,
header_path: &Path,
output_path: &Path,
options: &ClangOptions,
) -> Result<X86PCHFile, X86PCHModulesError> {
let header_content = fs::read_to_string(header_path)?;
let lexer = Lexer::new(&header_content, CLangStandard::C17);
self.build_identifier_table(&header_content);
self.build_macro_definitions(&header_content);
self.build_source_files(header_path, &header_content);
let content_hash = self.compute_content_hash(
&header_content,
&self.identifier_table,
&self.macro_definitions,
);
let timestamp = SystemTime::now();
let header = X86PCHHeader {
magic: *X86_PCH_MAGIC,
version_major: X86_PCH_VERSION_MAJOR,
version_minor: X86_PCH_VERSION_MINOR,
target_triple: options.target_triple.clone(),
language_standard: options.standard,
is_cxx: false,
file_size: 0, timestamp,
content_hash,
num_identifiers: self.identifier_table.len() as u32,
num_source_files: self.source_files.len() as u32,
num_macros: self.macro_definitions.len() as u32,
num_declarations: 0,
block_offsets: HashMap::new(),
};
let file_size = self.write_pch_file(output_path, &header, options)?;
let pch_file = X86PCHFile {
path: output_path.to_path_buf(),
header: X86PCHHeader {
file_size,
..header
},
validated: false,
chain_index: 0,
kind: X86PCHKind::Project,
dependencies: vec![header_path.to_path_buf()],
content_hash,
};
if self.debug_log {
eprintln!(
"PCH generated: {} ({} bytes, {} identifiers, {} macros)",
output_path.display(),
file_size,
self.identifier_table.len(),
self.macro_definitions.len()
);
}
Ok(pch_file)
}
fn build_identifier_table(&mut self, content: &str) {
self.identifier_table.clear();
let keywords = [
"int", "char", "float", "double", "void", "struct", "enum", "union", "typedef",
"static", "extern", "const", "volatile", "unsigned", "signed", "long", "short",
"sizeof", "if", "else", "for", "while", "do", "switch", "case", "default", "break",
"continue", "return", "goto", "auto", "register", "inline", "restrict",
];
for (i, kw) in keywords.iter().enumerate() {
self.identifier_table.insert(kw.to_string(), i as u32 + 1);
}
}
fn build_macro_definitions(&mut self, content: &str) {
self.macro_definitions.clear();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("#define ") {
let rest = &trimmed[8..];
if let Some(space_idx) = rest.find(|c: char| c.is_whitespace() || c == '(') {
let name = &rest[..space_idx];
let body = rest[space_idx..].trim().to_string();
let is_function_like = rest.as_bytes().get(space_idx) == Some(&b'(');
let parameters = if is_function_like {
if let Some(close) = rest[space_idx..].find(')') {
let param_str = &rest[space_idx + 1..space_idx + close];
param_str
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
} else {
Vec::new()
}
} else {
Vec::new()
};
let is_variadic = body.contains("...") || name.ends_with("...");
self.macro_definitions.insert(
name.to_string(),
MacroDefinitionData {
name: name.to_string(),
body,
is_function_like,
parameters,
is_builtin: false,
is_variadic,
source_location: SourceLocation::default(),
is_defined: true,
},
);
}
}
}
}
fn build_source_files(&mut self, header_path: &Path, content: &str) {
self.source_files.clear();
let content_hash = self.hash_content(content.as_bytes());
let metadata = fs::metadata(header_path).ok();
let entry = X86SourceFileEntry {
path: header_path.to_path_buf(),
size: content.len() as u64,
mtime: metadata
.and_then(|m| m.modified().ok())
.unwrap_or(UNIX_EPOCH),
content_hash,
is_system: false,
is_extern_c: false,
line_table: self.build_line_table(content),
};
self.source_files.push(entry);
}
fn build_line_table(&mut self, content: &str) -> Vec<X86LineEntry> {
let mut entries = Vec::new();
let mut offset: u64 = 0;
let mut line: u32 = 1;
entries.push(X86LineEntry {
file_offset: 0,
line: 1,
column: 0,
is_line_directive: false,
});
for ch in content.bytes() {
if ch == b'\n' {
line += 1;
entries.push(X86LineEntry {
file_offset: offset + 1,
line,
column: 0,
is_line_directive: false,
});
}
offset += 1;
}
entries
}
fn compute_content_hash(
&self,
content: &str,
identifiers: &HashMap<String, u32>,
macros: &HashMap<String, MacroDefinitionData>,
) -> [u8; 32] {
let mut combined = Vec::new();
combined.extend_from_slice(content.as_bytes());
for (name, _id) in identifiers {
combined.extend_from_slice(name.as_bytes());
}
for (name, mdef) in macros {
combined.extend_from_slice(name.as_bytes());
combined.extend_from_slice(mdef.body.as_bytes());
}
self.hash_content(&combined)
}
fn hash_content(&self, data: &[u8]) -> [u8; 32] {
let mut hash = [0u8; 32];
let mut state: u64 = 0x6a09e667f3bcc908;
for (i, &byte) in data.iter().enumerate() {
state = state.wrapping_mul(0x9e3779b97f4a7c15);
state = state.wrapping_add(byte as u64);
state = state.wrapping_add((i as u64).wrapping_mul(0xbf58476d1ce4e5b9));
hash[(i % 32) as usize] ^= (state >> ((i % 8) * 8)) as u8;
}
hash[0..8].copy_from_slice(&state.to_le_bytes());
hash
}
fn write_pch_file(
&mut self,
output_path: &Path,
header: &X86PCHHeader,
options: &ClangOptions,
) -> Result<u64, X86PCHModulesError> {
let file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(output_path)?;
let mut writer = BufWriter::with_capacity(64 * 1024, file);
writer.write_all(X86_PCH_MAGIC)?;
writer.write_all(&X86_PCH_VERSION_MAJOR.to_le_bytes())?;
writer.write_all(&X86_PCH_VERSION_MINOR.to_le_bytes())?;
let triple_bytes = header.target_triple.as_bytes();
writer.write_all(&(triple_bytes.len() as u32).to_le_bytes())?;
writer.write_all(triple_bytes)?;
let std_id = match header.language_standard {
CLangStandard::C89 => 0u32,
CLangStandard::C99 => 2,
CLangStandard::C11 => 5,
CLangStandard::C17 => 7,
CLangStandard::C23 => 8,
CLangStandard::Gnu89 => 1,
CLangStandard::Gnu99 => 3,
CLangStandard::Gnu11 => 6,
CLangStandard::Gnu17 => 9,
};
writer.write_all(&std_id.to_le_bytes())?;
writer.write_all(&(header.is_cxx as u8).to_le_bytes())?;
writer.write_all(&header.content_hash)?;
writer.write_all(&header.num_identifiers.to_le_bytes())?;
writer.write_all(&header.num_source_files.to_le_bytes())?;
writer.write_all(&header.num_macros.to_le_bytes())?;
let control_offset = writer.stream_position()?;
self.write_control_block(&mut writer)?;
let identifier_offset = writer.stream_position()?;
self.write_identifier_block(&mut writer)?;
let source_manager_offset = writer.stream_position()?;
self.write_source_manager_block(&mut writer)?;
let preprocessor_offset = writer.stream_position()?;
self.write_preprocessor_block(&mut writer)?;
let ast_offset = writer.stream_position()?;
self.write_ast_block(&mut writer)?;
let input_file_offset = writer.stream_position()?;
self.write_input_file_block(&mut writer)?;
writer.flush()?;
let total_size = writer.stream_position()?;
self.block_offsets
.insert(X86_PCH_BLOCK_CONTROL, control_offset);
self.block_offsets
.insert(X86_PCH_BLOCK_IDENTIFIER, identifier_offset);
self.block_offsets
.insert(X86_PCH_BLOCK_SOURCE_MANAGER, source_manager_offset);
self.block_offsets
.insert(X86_PCH_BLOCK_PREPROCESSOR, preprocessor_offset);
self.block_offsets.insert(X86_PCH_BLOCK_AST, ast_offset);
self.block_offsets
.insert(X86_PCH_BLOCK_INPUT_FILES, input_file_offset);
Ok(total_size)
}
fn write_control_block(&self, writer: &mut (impl Write + Seek)) -> io::Result<()> {
writer.write_all(&X86_PCH_BLOCK_CONTROL.to_le_bytes())?;
let block_start = writer.stream_position()?;
writer.write_all(&[0u8; 8])?;
self.write_control_record(writer, 0x0001, b"X86 PCH v17.0")?;
self.write_control_record(writer, 0x0002, b"llvm-native-core")?;
writer.write_all(&[0u8; 4])?;
let block_end = writer.stream_position()?;
let block_size = block_end - block_start - 8;
writer.seek(SeekFrom::Start(block_start))?;
writer.write_all(&(block_size as u64).to_le_bytes())?;
writer.seek(SeekFrom::End(0))?;
Ok(())
}
fn write_identifier_block(&self, writer: &mut (impl Write + Seek)) -> io::Result<()> {
writer.write_all(&X86_PCH_BLOCK_IDENTIFIER.to_le_bytes())?;
let block_start = writer.stream_position()?;
writer.write_all(&[0u8; 8])?;
writer.write_all(&(self.identifier_table.len() as u32).to_le_bytes())?;
let mut sorted: Vec<_> = self.identifier_table.iter().collect();
sorted.sort_by_key(|(name, _)| *name);
for &(ref name, &id) in &sorted {
writer.write_all(&id.to_le_bytes())?;
writer.write_all(&(name.len() as u32).to_le_bytes())?;
writer.write_all(name.as_bytes())?;
let hash = self.hash_content(name.as_bytes());
writer.write_all(&hash[..8])?;
}
writer.write_all(&[0u8; 4])?;
let block_end = writer.stream_position()?;
let block_size = block_end - block_start - 8;
writer.seek(SeekFrom::Start(block_start))?;
writer.write_all(&(block_size as u64).to_le_bytes())?;
writer.seek(SeekFrom::End(0))?;
Ok(())
}
fn write_source_manager_block(&self, writer: &mut (impl Write + Seek)) -> io::Result<()> {
writer.write_all(&X86_PCH_BLOCK_SOURCE_MANAGER.to_le_bytes())?;
let block_start = writer.stream_position()?;
writer.write_all(&[0u8; 8])?;
writer.write_all(&(self.source_files.len() as u32).to_le_bytes())?;
for entry in &self.source_files {
let path_str = entry.path.to_string_lossy();
writer.write_all(&(path_str.len() as u32).to_le_bytes())?;
writer.write_all(path_str.as_bytes())?;
writer.write_all(&entry.size.to_le_bytes())?;
let mtime_secs = entry
.mtime
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
writer.write_all(&mtime_secs.to_le_bytes())?;
writer.write_all(&entry.content_hash)?;
let flags = (entry.is_system as u8) | ((entry.is_extern_c as u8) << 1);
writer.write_all(&flags.to_le_bytes())?;
writer.write_all(&(entry.line_table.len() as u32).to_le_bytes())?;
for line_entry in &entry.line_table {
writer.write_all(&line_entry.file_offset.to_le_bytes())?;
writer.write_all(&line_entry.line.to_le_bytes())?;
writer.write_all(&line_entry.column.to_le_bytes())?;
writer.write_all(&(line_entry.is_line_directive as u8).to_le_bytes())?;
}
}
writer.write_all(&[0u8; 4])?;
let block_end = writer.stream_position()?;
let block_size = block_end - block_start - 8;
writer.seek(SeekFrom::Start(block_start))?;
writer.write_all(&(block_size as u64).to_le_bytes())?;
writer.seek(SeekFrom::End(0))?;
Ok(())
}
fn write_preprocessor_block(&self, writer: &mut (impl Write + Seek)) -> io::Result<()> {
writer.write_all(&X86_PCH_BLOCK_PREPROCESSOR.to_le_bytes())?;
let block_start = writer.stream_position()?;
writer.write_all(&[0u8; 8])?;
writer.write_all(&(self.macro_definitions.len() as u32).to_le_bytes())?;
let mut sorted: Vec<_> = self.macro_definitions.iter().collect();
sorted.sort_by_key(|(name, _)| *name);
for (name, mdef) in &sorted {
writer.write_all(&(name.len() as u32).to_le_bytes())?;
writer.write_all(name.as_bytes())?;
writer.write_all(&(mdef.body.len() as u32).to_le_bytes())?;
writer.write_all(mdef.body.as_bytes())?;
let flags = (mdef.is_function_like as u8)
| ((mdef.is_builtin as u8) << 1)
| ((mdef.is_variadic as u8) << 2)
| ((mdef.is_defined as u8) << 3);
writer.write_all(&flags.to_le_bytes())?;
writer.write_all(&(mdef.parameters.len() as u32).to_le_bytes())?;
for param in &mdef.parameters {
writer.write_all(&(param.len() as u32).to_le_bytes())?;
writer.write_all(param.as_bytes())?;
}
writer.write_all(&mdef.source_location.line.to_le_bytes())?;
writer.write_all(&mdef.source_location.column.to_le_bytes())?;
}
writer.write_all(&[0u8; 4])?;
let block_end = writer.stream_position()?;
let block_size = block_end - block_start - 8;
writer.seek(SeekFrom::Start(block_start))?;
writer.write_all(&(block_size as u64).to_le_bytes())?;
writer.seek(SeekFrom::End(0))?;
Ok(())
}
fn write_ast_block(&self, writer: &mut (impl Write + Seek)) -> io::Result<()> {
writer.write_all(&X86_PCH_BLOCK_AST.to_le_bytes())?;
let block_start = writer.stream_position()?;
writer.write_all(&[0u8; 8])?;
writer.write_all(&(ASTRecordType::DeclTranslationUnit as u32).to_le_bytes())?;
writer.write_all(&0u32.to_le_bytes())?;
writer.write_all(&[0u8; 4])?;
let block_end = writer.stream_position()?;
let block_size = block_end - block_start - 8;
writer.seek(SeekFrom::Start(block_start))?;
writer.write_all(&(block_size as u64).to_le_bytes())?;
writer.seek(SeekFrom::End(0))?;
Ok(())
}
fn write_input_file_block(&self, writer: &mut (impl Write + Seek)) -> io::Result<()> {
writer.write_all(&X86_PCH_BLOCK_INPUT_FILES.to_le_bytes())?;
let block_start = writer.stream_position()?;
writer.write_all(&[0u8; 8])?;
writer.write_all(&(self.source_files.len() as u32).to_le_bytes())?;
for entry in &self.source_files {
let path_str = entry.path.to_string_lossy();
writer.write_all(&(path_str.len() as u32).to_le_bytes())?;
writer.write_all(path_str.as_bytes())?;
writer.write_all(&entry.content_hash)?;
writer.write_all(&entry.size.to_le_bytes())?;
}
writer.write_all(&[0u8; 4])?;
let block_end = writer.stream_position()?;
let block_size = block_end - block_start - 8;
writer.seek(SeekFrom::Start(block_start))?;
writer.write_all(&(block_size as u64).to_le_bytes())?;
writer.seek(SeekFrom::End(0))?;
Ok(())
}
fn write_control_record(
&self,
writer: &mut impl Write,
record_id: u32,
data: &[u8],
) -> io::Result<()> {
writer.write_all(&record_id.to_le_bytes())?;
writer.write_all(&(data.len() as u32).to_le_bytes())?;
writer.write_all(data)?;
Ok(())
}
}
pub struct X86PCHValidator {
pub use_stat_cache: bool,
pub validate_input_hashes: bool,
pub validate_macros: bool,
pub max_pch_age: Option<Duration>,
}
impl X86PCHValidator {
pub fn new() -> Self {
Self {
use_stat_cache: true,
validate_input_hashes: true,
validate_macros: true,
max_pch_age: None,
}
}
pub fn validate(
&self,
pch_path: &Path,
options: &ClangOptions,
stat_cache: &mut X86PCHStatCache,
) -> Result<X86PCHValidationResult, X86PCHModulesError> {
let start = std::time::Instant::now();
if self.use_stat_cache {
if let Some(cached) = stat_cache.lookup(pch_path) {
return Ok(X86PCHValidationResult {
is_valid: cached.is_valid,
failure_reason: cached.failure_reason.clone(),
language_options_match: true,
target_options_match: true,
header_search_paths_match: true,
stat_cache_used: true,
duration_ms: start.elapsed().as_millis() as u64,
});
}
}
let mut file = File::open(pch_path)?;
let mut magic = [0u8; 4];
file.read_exact(&mut magic)?;
if &magic != X86_PCH_MAGIC {
return Ok(X86PCHValidationResult {
is_valid: false,
failure_reason: Some("Invalid PCH magic".to_string()),
language_options_match: false,
target_options_match: false,
header_search_paths_match: false,
stat_cache_used: false,
duration_ms: start.elapsed().as_millis() as u64,
});
}
let mut ver_buf = [0u8; 4];
file.read_exact(&mut ver_buf)?;
let version_major = u32::from_le_bytes(ver_buf);
file.read_exact(&mut ver_buf)?;
let version_minor = u32::from_le_bytes(ver_buf);
if version_major != X86_PCH_VERSION_MAJOR {
return Ok(X86PCHValidationResult {
is_valid: false,
failure_reason: Some(format!(
"PCH version mismatch: expected {}.{}, got {}.{}",
X86_PCH_VERSION_MAJOR, X86_PCH_VERSION_MINOR, version_major, version_minor
)),
language_options_match: false,
target_options_match: false,
header_search_paths_match: false,
stat_cache_used: false,
duration_ms: start.elapsed().as_millis() as u64,
});
}
file.read_exact(&mut ver_buf)?;
let triple_len = u32::from_le_bytes(ver_buf) as usize;
let mut triple_bytes = vec![0u8; triple_len.min(256)];
file.read_exact(&mut triple_bytes)?;
let pch_triple = String::from_utf8_lossy(&triple_bytes);
let target_options_match = pch_triple == options.target_triple;
file.read_exact(&mut ver_buf)?;
let _pch_std_id = u32::from_le_bytes(ver_buf);
let language_options_match = true;
if let Ok(metadata) = fs::metadata(pch_path) {
if let Ok(mtime) = metadata.modified() {
if let Some(max_age) = self.max_pch_age {
if mtime.elapsed().unwrap_or_default() > max_age {
return Ok(X86PCHValidationResult {
is_valid: false,
failure_reason: Some("PCH file is too old".to_string()),
language_options_match,
target_options_match,
header_search_paths_match: true,
stat_cache_used: false,
duration_ms: start.elapsed().as_millis() as u64,
});
}
}
}
}
let result = X86PCHValidationResult {
is_valid: target_options_match && language_options_match,
failure_reason: if !target_options_match {
Some(format!(
"Target triple mismatch: expected {}, found {}",
options.target_triple, pch_triple
))
} else {
None
},
language_options_match,
target_options_match,
header_search_paths_match: true,
stat_cache_used: false,
duration_ms: start.elapsed().as_millis() as u64,
};
if self.use_stat_cache {
stat_cache.insert(
pch_path,
X86CachedPCHValidation {
is_valid: result.is_valid,
failure_reason: result.failure_reason.clone(),
validated_at: SystemTime::now(),
},
);
}
Ok(result)
}
}
#[derive(Debug, Clone)]
pub struct X86ASTReader;
impl X86ASTReader {
pub fn new() -> Self {
Self
}
}
pub struct X86PCHLoader {
pub ast_reader: X86ASTReader,
pub lazy_loading: bool,
pub pre_validate: bool,
pub max_memory: u64,
}
impl X86PCHLoader {
pub fn new() -> Self {
Self {
ast_reader: X86ASTReader::new(),
lazy_loading: true,
pre_validate: true,
max_memory: 512 * 1024 * 1024, }
}
pub fn load(
&self,
pch_path: &Path,
stat_cache: &mut X86PCHStatCache,
) -> Result<X86PCHLoadResult, X86PCHModulesError> {
let mut file = File::open(pch_path)?;
let file_size = file.metadata()?.len();
let mut warnings = Vec::new();
let mut magic = [0u8; 4];
file.read_exact(&mut magic)?;
if &magic != X86_PCH_MAGIC {
return Err(X86PCHModulesError::InvalidPCHFormat(
"Bad magic number".to_string(),
));
}
let mut ver_buf = [0u8; 4];
file.read_exact(&mut ver_buf)?;
let version_major = u32::from_le_bytes(ver_buf);
file.read_exact(&mut ver_buf)?;
let _version_minor = u32::from_le_bytes(ver_buf);
if version_major != X86_PCH_VERSION_MAJOR {
warnings.push(format!(
"PCH version mismatch: expected {}, got {}",
X86_PCH_VERSION_MAJOR, version_major
));
}
file.read_exact(&mut ver_buf)?;
let triple_len = u32::from_le_bytes(ver_buf) as usize;
file.seek(SeekFrom::Current(triple_len.min(256) as i64))?;
file.seek(SeekFrom::Current(5))?;
let mut content_hash = [0u8; 32];
file.read_exact(&mut content_hash)?;
file.read_exact(&mut ver_buf)?;
let num_identifiers = u32::from_le_bytes(ver_buf) as usize;
file.read_exact(&mut ver_buf)?;
let num_source_files = u32::from_le_bytes(ver_buf) as usize;
file.read_exact(&mut ver_buf)?;
let _num_macros = u32::from_le_bytes(ver_buf) as usize;
let mut identifiers = HashMap::new();
let mut macros = HashMap::new();
let mut source_files = Vec::new();
let mut ast = None;
loop {
let mut block_id_buf = [0u8; 4];
if file.read_exact(&mut block_id_buf).is_err() {
break;
}
let block_id = u32::from_le_bytes(block_id_buf);
let mut size_buf = [0u8; 8];
if file.read_exact(&mut size_buf).is_err() {
break;
}
let block_size = u64::from_le_bytes(size_buf);
match block_id {
X86_PCH_BLOCK_IDENTIFIER => {
identifiers = self.read_identifier_block(&mut file, block_size)?;
}
X86_PCH_BLOCK_PREPROCESSOR => {
macros = self.read_preprocessor_block(&mut file, block_size)?;
}
X86_PCH_BLOCK_SOURCE_MANAGER => {
source_files = self.read_source_manager_block(&mut file, block_size)?;
}
X86_PCH_BLOCK_AST => {
file.seek(SeekFrom::Current(block_size as i64))?;
}
X86_PCH_BLOCK_CONTROL | X86_PCH_BLOCK_INPUT_FILES => {
file.seek(SeekFrom::Current(block_size as i64))?;
}
_ => {
file.seek(SeekFrom::Current(block_size as i64))?;
}
}
let mut term = [0u8; 4];
let _ = file.read_exact(&mut term);
}
Ok(X86PCHLoadResult {
ast,
identifiers,
macros,
source_files,
preprocessor_state: None,
bytes_read: file_size,
success: true,
warnings,
})
}
fn read_identifier_block(
&self,
file: &mut File,
_block_size: u64,
) -> Result<HashMap<String, u32>, X86PCHModulesError> {
let mut identifiers = HashMap::new();
let mut count_buf = [0u8; 4];
file.read_exact(&mut count_buf)?;
let count = u32::from_le_bytes(count_buf) as usize;
for _ in 0..count.min(X86_MAX_IDENTIFIERS) {
let mut id_buf = [0u8; 4];
file.read_exact(&mut id_buf)?;
let id = u32::from_le_bytes(id_buf);
file.read_exact(&mut id_buf)?;
let name_len = u32::from_le_bytes(id_buf) as usize;
let name_len = name_len.min(1024);
let mut name_bytes = vec![0u8; name_len];
file.read_exact(&mut name_bytes)?;
let name = String::from_utf8_lossy(&name_bytes).to_string();
let mut hash_buf = [0u8; 8];
file.read_exact(&mut hash_buf)?;
identifiers.insert(name, id);
}
Ok(identifiers)
}
fn read_preprocessor_block(
&self,
file: &mut File,
_block_size: u64,
) -> Result<HashMap<String, MacroDefinitionData>, X86PCHModulesError> {
let mut macros = HashMap::new();
let mut count_buf = [0u8; 4];
file.read_exact(&mut count_buf)?;
let count = u32::from_le_bytes(count_buf) as usize;
for _ in 0..count {
let mut len_buf = [0u8; 4];
file.read_exact(&mut len_buf)?;
let name_len = u32::from_le_bytes(len_buf) as usize;
let name_len = name_len.min(1024);
let mut name_bytes = vec![0u8; name_len];
file.read_exact(&mut name_bytes)?;
let name = String::from_utf8_lossy(&name_bytes).to_string();
file.read_exact(&mut len_buf)?;
let body_len = u32::from_le_bytes(len_buf) as usize;
let body_len = body_len.min(65536);
let mut body_bytes = vec![0u8; body_len];
file.read_exact(&mut body_bytes)?;
let body = String::from_utf8_lossy(&body_bytes).to_string();
let mut flag_buf = [0u8; 1];
file.read_exact(&mut flag_buf)?;
let flags = flag_buf[0];
let is_function_like = (flags & 0x01) != 0;
let is_builtin = (flags & 0x02) != 0;
let is_variadic = (flags & 0x04) != 0;
let is_defined = (flags & 0x08) != 0;
file.read_exact(&mut len_buf)?;
let param_count = u32::from_le_bytes(len_buf) as usize;
let mut parameters = Vec::with_capacity(param_count.min(64));
for _ in 0..param_count.min(64) {
file.read_exact(&mut len_buf)?;
let plen = u32::from_le_bytes(len_buf) as usize;
let plen = plen.min(256);
let mut pbytes = vec![0u8; plen];
file.read_exact(&mut pbytes)?;
parameters.push(String::from_utf8_lossy(&pbytes).to_string());
}
let mut loc_buf = [0u8; 4];
file.read_exact(&mut loc_buf)?;
let line = u32::from_le_bytes(loc_buf);
file.read_exact(&mut loc_buf)?;
let column = u32::from_le_bytes(loc_buf);
macros.insert(
name.clone(),
MacroDefinitionData {
name,
body,
is_function_like,
parameters,
is_builtin,
is_variadic,
source_location: SourceLocation {
line,
column,
..Default::default()
},
is_defined,
},
);
}
Ok(macros)
}
fn read_source_manager_block(
&self,
file: &mut File,
_block_size: u64,
) -> Result<Vec<X86SourceFileEntry>, X86PCHModulesError> {
let mut entries = Vec::new();
let mut count_buf = [0u8; 4];
file.read_exact(&mut count_buf)?;
let count = u32::from_le_bytes(count_buf) as usize;
for _ in 0..count.min(X86_MAX_SOURCE_FILES) {
let mut len_buf = [0u8; 4];
file.read_exact(&mut len_buf)?;
let path_len = u32::from_le_bytes(len_buf) as usize;
let path_len = path_len.min(4096);
let mut path_bytes = vec![0u8; path_len];
file.read_exact(&mut path_bytes)?;
let path = PathBuf::from(String::from_utf8_lossy(&path_bytes).as_ref());
let mut size_buf = [0u8; 8];
file.read_exact(&mut size_buf)?;
let size = u64::from_le_bytes(size_buf);
file.read_exact(&mut len_buf)?;
let mtime_secs = u64::from(u32::from_le_bytes(len_buf));
let mtime = UNIX_EPOCH + Duration::from_secs(mtime_secs);
let mut hash = [0u8; 32];
file.read_exact(&mut hash)?;
let mut flag_byte = [0u8; 1];
file.read_exact(&mut flag_byte)?;
let flags = flag_byte[0];
let is_system = (flags & 0x01) != 0;
let is_extern_c = (flags & 0x02) != 0;
file.read_exact(&mut len_buf)?;
let line_count = u32::from_le_bytes(len_buf) as usize;
let mut line_table = Vec::with_capacity(line_count.min(100000));
for _ in 0..line_count.min(100000) {
let mut buf8 = [0u8; 8];
let mut buf4 = [0u8; 4];
file.read_exact(&mut buf8)?;
let file_offset = u64::from_le_bytes(buf8);
file.read_exact(&mut buf4)?;
let line = u32::from_le_bytes(buf4);
file.read_exact(&mut buf4)?;
let column = u32::from_le_bytes(buf4);
file.read_exact(&mut buf4)?;
let is_line_dir = u32::from_le_bytes(buf4) != 0;
line_table.push(X86LineEntry {
file_offset,
line,
column,
is_line_directive: is_line_dir,
});
}
entries.push(X86SourceFileEntry {
path,
size,
mtime,
content_hash: hash,
is_system,
is_extern_c,
line_table,
});
}
Ok(entries)
}
}
pub struct X86PCHStatCache {
entries: HashMap<PathBuf, X86CachedPCHValidation>,
ttl: Duration,
max_entries: usize,
hits: u64,
misses: u64,
}
#[derive(Debug, Clone)]
pub struct X86CachedPCHValidation {
pub is_valid: bool,
pub failure_reason: Option<String>,
pub validated_at: SystemTime,
}
impl X86PCHStatCache {
pub fn new() -> Self {
Self {
entries: HashMap::new(),
ttl: Duration::from_secs(X86_STAT_CACHE_TTL_SECS),
max_entries: 10_000,
hits: 0,
misses: 0,
}
}
pub fn with_ttl(ttl_secs: u64) -> Self {
Self {
entries: HashMap::new(),
ttl: Duration::from_secs(ttl_secs),
max_entries: 10_000,
hits: 0,
misses: 0,
}
}
pub fn lookup(&mut self, pch_path: &Path) -> Option<&X86CachedPCHValidation> {
let now = SystemTime::now();
let is_fresh = self.entries.get(pch_path).map_or(false, |entry| {
now.duration_since(entry.validated_at).unwrap_or_default() < self.ttl
});
if is_fresh {
self.hits += 1;
return self.entries.get(pch_path);
}
if self.entries.contains_key(pch_path) {
self.entries.remove(pch_path);
}
self.misses += 1;
None
}
pub fn insert(&mut self, pch_path: &Path, validation: X86CachedPCHValidation) {
if self.entries.len() >= self.max_entries {
if let Some(oldest) = self
.entries
.iter()
.min_by_key(|(_, v)| v.validated_at)
.map(|(k, _)| k.clone())
{
self.entries.remove(&oldest);
}
}
self.entries.insert(pch_path.to_path_buf(), validation);
}
pub fn invalidate(&mut self, pch_path: &Path) {
self.entries.remove(pch_path);
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn hits(&self) -> u64 {
self.hits
}
pub fn misses(&self) -> u64 {
self.misses
}
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
0.0
} else {
self.hits as f64 / total as f64
}
}
}
pub struct X86PCHChaining {
pub chain: Vec<X86PCHFile>,
pub validate_chain: bool,
pub merge_identifiers: bool,
pub merge_macros: bool,
}
impl X86PCHChaining {
pub fn new() -> Self {
Self {
chain: Vec::new(),
validate_chain: true,
merge_identifiers: true,
merge_macros: true,
}
}
pub fn chain(
&mut self,
pch_paths: &[PathBuf],
options: &ClangOptions,
stat_cache: &mut X86PCHStatCache,
) -> Result<Vec<X86PCHFile>, X86PCHModulesError> {
if pch_paths.len() > X86_PCH_MAX_CHAIN_DEPTH as usize {
return Err(X86PCHModulesError::ChainDepthExceeded);
}
let mut chain = Vec::new();
let validator = X86PCHValidator::new();
let loader = X86PCHLoader::new();
for (i, path) in pch_paths.iter().enumerate() {
let validation = validator.validate(path, options, stat_cache)?;
if !validation.is_valid {
return Err(X86PCHModulesError::ChainingError(format!(
"PCH chain validation failed at index {} ({}): {:?}",
i,
path.display(),
validation.failure_reason
)));
}
let load_result = loader.load(path, stat_cache)?;
let content_hash = load_result
.source_files
.first()
.map_or([0u8; 32], |e| e.content_hash);
let kind = if i == 0 {
X86PCHKind::System
} else {
X86PCHKind::Project
};
let mut dependencies = Vec::new();
if i > 0 {
dependencies.push(pch_paths[i - 1].clone());
}
dependencies.push(path.clone());
let header = X86PCHHeader {
magic: *X86_PCH_MAGIC,
version_major: X86_PCH_VERSION_MAJOR,
version_minor: X86_PCH_VERSION_MINOR,
target_triple: options.target_triple.clone(),
language_standard: options.standard,
is_cxx: false,
file_size: fs::metadata(path)?.len(),
timestamp: SystemTime::now(),
content_hash,
num_identifiers: load_result.identifiers.len() as u32,
num_source_files: load_result.source_files.len() as u32,
num_macros: load_result.macros.len() as u32,
num_declarations: 0,
block_offsets: HashMap::new(),
};
chain.push(X86PCHFile {
path: path.clone(),
header,
validated: true,
chain_index: i as u32,
kind,
dependencies,
content_hash,
});
}
self.chain = chain.clone();
Ok(chain)
}
pub fn total_identifiers(&self) -> usize {
self.chain
.iter()
.map(|p| p.header.num_identifiers as usize)
.sum()
}
pub fn total_macros(&self) -> usize {
self.chain
.iter()
.map(|p| p.header.num_macros as usize)
.sum()
}
pub fn depth(&self) -> usize {
self.chain.len()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleInterface {
pub module_name: ModuleName,
pub is_partition: bool,
pub partition_name: Option<String>,
pub bmi_path: PathBuf,
pub source_path: PathBuf,
pub exported_declarations: Vec<String>,
pub imported_modules: Vec<ModuleName>,
pub track_reachability: bool,
pub content_hash: [u8; 32],
pub is_valid: bool,
pub compiled_at: SystemTime,
}
pub struct X86ModuleInterfaceCompiler {
pub validate_exports: bool,
pub track_reachability: bool,
pub emit_debug_info: bool,
pub check_odr: bool,
}
impl X86ModuleInterfaceCompiler {
pub fn new() -> Self {
Self {
validate_exports: true,
track_reachability: true,
emit_debug_info: false,
check_odr: true,
}
}
pub fn compile(
&self,
source_path: &Path,
module_name: &ModuleName,
output_path: &Path,
options: &ClangOptions,
) -> Result<X86ModuleInterface, X86PCHModulesError> {
let content = fs::read_to_string(source_path)?;
let exported = self.extract_export_declarations(&content);
let imports = self.extract_import_declarations(&content);
let is_partition = module_name.components.last().map_or(false, |c| {
c.starts_with(':') || content.contains(":partition")
});
let partition_name = if is_partition {
module_name.components.last().cloned()
} else {
None
};
let content_hash = {
let mut hash = [0u8; 32];
let mut state: u64 = 0x6a09e667f3bcc908;
for (i, &byte) in content.as_bytes().iter().enumerate() {
state = state.wrapping_mul(0x9e3779b97f4a7c15);
state = state.wrapping_add(byte as u64);
state = state.wrapping_add((i as u64).wrapping_mul(0xbf58476d1ce4e5b9));
hash[(i % 32) as usize] ^= (state >> ((i % 8) * 8)) as u8;
}
hash[0..8].copy_from_slice(&state.to_le_bytes());
hash
};
let interface = X86ModuleInterface {
module_name: module_name.clone(),
is_partition,
partition_name,
bmi_path: output_path.to_path_buf(),
source_path: source_path.to_path_buf(),
exported_declarations: exported,
imported_modules: imports,
track_reachability: self.track_reachability,
content_hash,
is_valid: true,
compiled_at: SystemTime::now(),
};
Ok(interface)
}
fn extract_export_declarations(&self, content: &str) -> Vec<String> {
let mut exports = Vec::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("export ") {
if let Some(rest) = trimmed.strip_prefix("export ") {
let decl = rest.trim();
if decl.starts_with("module ") {
continue; }
exports.push(decl.to_string());
}
}
}
exports
}
fn extract_import_declarations(&self, content: &str) -> Vec<ModuleName> {
let mut imports = Vec::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("import ") && !trimmed.starts_with("import <") {
if let Some(rest) = trimmed.strip_prefix("import ") {
let name = rest.trim_end_matches(';').trim();
if !name.is_empty() {
imports.push(ModuleName::from_str(name));
}
}
}
}
imports
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleImplementation {
pub module_name: ModuleName,
pub is_partition: bool,
pub source_path: PathBuf,
pub output_path: PathBuf,
pub implicit_import: ModuleName,
pub content_hash: [u8; 32],
pub is_valid: bool,
pub linkage_flags: X86ModuleLinkageFlags,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct X86ModuleLinkageFlags {
pub internal_linkage: bool,
pub has_abi: bool,
pub weak_symbols: bool,
}
impl Default for X86ModuleLinkageFlags {
fn default() -> Self {
Self {
internal_linkage: false,
has_abi: true,
weak_symbols: false,
}
}
}
pub struct X86ModuleImplCompiler {
pub implicit_import: bool,
pub validate_against_interface: bool,
pub pic: bool,
}
impl X86ModuleImplCompiler {
pub fn new() -> Self {
Self {
implicit_import: true,
validate_against_interface: true,
pic: true,
}
}
pub fn compile(
&self,
source_path: &Path,
module_name: &ModuleName,
output_path: &Path,
options: &ClangOptions,
) -> Result<X86ModuleImplementation, X86PCHModulesError> {
let content = fs::read_to_string(source_path)?;
let content_hash = {
let mut hash = [0u8; 32];
let mut state: u64 = 0x6a09e667f3bcc908;
for (i, &byte) in content.as_bytes().iter().enumerate() {
state = state.wrapping_mul(0x9e3779b97f4a7c15);
state = state.wrapping_add(byte as u64);
state = state.wrapping_add((i as u64).wrapping_mul(0xbf58476d1ce4e5b9));
hash[(i % 32) as usize] ^= (state >> ((i % 8) * 8)) as u8;
}
hash[0..8].copy_from_slice(&state.to_le_bytes());
hash
};
Ok(X86ModuleImplementation {
module_name: module_name.clone(),
is_partition: module_name
.components
.last()
.map_or(false, |c| c.contains(':')),
source_path: source_path.to_path_buf(),
output_path: output_path.to_path_buf(),
implicit_import: module_name.clone(),
content_hash,
is_valid: true,
linkage_flags: X86ModuleLinkageFlags::default(),
})
}
}
pub struct X86BMISerializer {
pub format_version: u32,
pub compress: bool,
pub include_source_locations: bool,
pub include_dependencies: bool,
}
impl X86BMISerializer {
pub fn new() -> Self {
Self {
format_version: X86_BMI_EXTENDED_VERSION,
compress: false,
include_source_locations: true,
include_dependencies: true,
}
}
pub fn serialize(
&self,
interface: &X86ModuleInterface,
output_path: &Path,
) -> Result<u64, X86PCHModulesError> {
let file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(output_path)?;
let mut writer = BufWriter::with_capacity(64 * 1024, file);
writer.write_all(X86_BMI_SIGNATURE_MAGIC)?;
writer.write_all(&self.format_version.to_le_bytes())?;
let name_str = interface.module_name.to_string();
writer.write_all(&(name_str.len() as u32).to_le_bytes())?;
writer.write_all(name_str.as_bytes())?;
writer.write_all(&(interface.is_partition as u8).to_le_bytes())?;
if let Some(ref part_name) = interface.partition_name {
writer.write_all(&(part_name.len() as u32).to_le_bytes())?;
writer.write_all(part_name.as_bytes())?;
} else {
writer.write_all(&0u32.to_le_bytes())?;
}
writer.write_all(&interface.content_hash)?;
writer.write_all(&(interface.exported_declarations.len() as u32).to_le_bytes())?;
for decl in &interface.exported_declarations {
writer.write_all(&(decl.len() as u32).to_le_bytes())?;
writer.write_all(decl.as_bytes())?;
}
if self.include_dependencies {
writer.write_all(&(interface.imported_modules.len() as u32).to_le_bytes())?;
for module in &interface.imported_modules {
let mname = module.to_string();
writer.write_all(&(mname.len() as u32).to_le_bytes())?;
writer.write_all(mname.as_bytes())?;
}
}
writer.flush()?;
let size = writer.stream_position()?;
Ok(size)
}
pub fn deserialize(&self, bmi_path: &Path) -> Result<X86ModuleInterface, X86PCHModulesError> {
let mut file = File::open(bmi_path)?;
let mut reader = BufReader::with_capacity(64 * 1024, file);
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
if &magic != X86_BMI_SIGNATURE_MAGIC {
return Err(X86PCHModulesError::BMISerializationFailed(
"Invalid BMI magic".to_string(),
));
}
let mut ver_buf = [0u8; 4];
reader.read_exact(&mut ver_buf)?;
let _version = u32::from_le_bytes(ver_buf);
reader.read_exact(&mut ver_buf)?;
let name_len = u32::from_le_bytes(ver_buf) as usize;
let mut name_bytes = vec![0u8; name_len.min(4096)];
reader.read_exact(&mut name_bytes)?;
let name_str = String::from_utf8_lossy(&name_bytes);
let module_name = ModuleName::from_str(&name_str);
let mut is_part_buf = [0u8; 1];
reader.read_exact(&mut is_part_buf)?;
let is_partition = is_part_buf[0] != 0;
reader.read_exact(&mut ver_buf)?;
let part_name_len = u32::from_le_bytes(ver_buf) as usize;
let partition_name = if part_name_len > 0 {
let mut part_bytes = vec![0u8; part_name_len.min(4096)];
reader.read_exact(&mut part_bytes)?;
Some(String::from_utf8_lossy(&part_bytes).to_string())
} else {
None
};
let mut content_hash = [0u8; 32];
reader.read_exact(&mut content_hash)?;
reader.read_exact(&mut ver_buf)?;
let export_count = u32::from_le_bytes(ver_buf) as usize;
let mut exported = Vec::with_capacity(export_count.min(100000));
for _ in 0..export_count.min(100000) {
reader.read_exact(&mut ver_buf)?;
let decl_len = u32::from_le_bytes(ver_buf) as usize;
let mut decl_bytes = vec![0u8; decl_len.min(65536)];
reader.read_exact(&mut decl_bytes)?;
exported.push(String::from_utf8_lossy(&decl_bytes).to_string());
}
let mut imported = Vec::new();
if reader.read_exact(&mut ver_buf).is_ok() {
let import_count = u32::from_le_bytes(ver_buf) as usize;
for _ in 0..import_count.min(10000) {
if reader.read_exact(&mut ver_buf).is_err() {
break;
}
let mlen = u32::from_le_bytes(ver_buf) as usize;
let mut mbytes = vec![0u8; mlen.min(4096)];
if reader.read_exact(&mut mbytes).is_err() {
break;
}
imported.push(ModuleName::from_str(&String::from_utf8_lossy(&mbytes)));
}
}
Ok(X86ModuleInterface {
module_name,
is_partition,
partition_name,
bmi_path: bmi_path.to_path_buf(),
source_path: PathBuf::new(),
exported_declarations: exported,
imported_modules: imported,
track_reachability: true,
content_hash,
is_valid: true,
compiled_at: SystemTime::now(),
})
}
}
#[derive(Debug, Clone)]
pub struct X86ParsedModuleMap {
pub path: PathBuf,
pub modules: Vec<X86ModuleMapEntry>,
pub is_framework: bool,
pub framework_name: Option<String>,
pub has_umbrella_header: bool,
pub umbrella_header: Option<PathBuf>,
pub has_submodules: bool,
}
#[derive(Debug, Clone)]
pub struct X86ModuleMapEntry {
pub name: String,
pub kind: X86ModuleMapKind,
pub headers: Vec<PathBuf>,
pub umbrella_header: Option<PathBuf>,
pub submodules: Vec<X86ModuleMapEntry>,
pub explicit: bool,
pub system: bool,
pub exports: Vec<String>,
pub uses: Vec<String>,
pub link_libraries: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ModuleMapKind {
Explicit,
Framework,
Umbrella,
Extern,
Virtual,
}
impl fmt::Display for X86ModuleMapKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Explicit => write!(f, "explicit"),
Self::Framework => write!(f, "framework"),
Self::Umbrella => write!(f, "umbrella"),
Self::Extern => write!(f, "extern"),
Self::Virtual => write!(f, "virtual"),
}
}
}
pub struct X86ModuleMapParser {
pub resolve_paths: bool,
pub base_directory: Option<PathBuf>,
pub validate_headers: bool,
}
impl X86ModuleMapParser {
pub fn new() -> Self {
Self {
resolve_paths: true,
base_directory: None,
validate_headers: false,
}
}
pub fn parse(&self, map_path: &Path) -> Result<X86ParsedModuleMap, X86PCHModulesError> {
let content = fs::read_to_string(map_path)?;
let base_dir = self
.base_directory
.clone()
.unwrap_or_else(|| map_path.parent().unwrap_or(Path::new(".")).to_path_buf());
let mut modules = Vec::new();
let mut is_framework = false;
let mut framework_name = None;
let mut umbrella_header = None;
let lines: Vec<&str> = content.lines().collect();
let mut i = 0;
while i < lines.len() {
let trimmed = lines[i].trim();
if trimmed.is_empty() || trimmed.starts_with("//") || trimmed.starts_with('#') {
i += 1;
continue;
}
let lower = trimmed.to_lowercase();
if lower.starts_with("framework module ") {
let rest = &trimmed[18..].trim();
let name = rest
.trim_end_matches('{')
.trim()
.trim_end_matches('{')
.trim();
framework_name = Some(name.to_string());
is_framework = true;
let mut entry = X86ModuleMapEntry {
name: name.to_string(),
kind: X86ModuleMapKind::Framework,
headers: Vec::new(),
umbrella_header: None,
submodules: Vec::new(),
explicit: false,
system: false,
exports: Vec::new(),
uses: Vec::new(),
link_libraries: Vec::new(),
};
i += 1;
while i < lines.len() {
let bline = lines[i].trim();
if bline == "}" {
break;
}
self.parse_module_map_line(bline, &mut entry, &base_dir);
i += 1;
}
modules.push(entry);
} else if lower.starts_with("module ") {
let rest = &trimmed[7..].trim();
let name = rest
.trim_end_matches('{')
.trim()
.trim_end_matches('{')
.trim();
let kind = if lower.contains("explicit") {
X86ModuleMapKind::Explicit
} else {
X86ModuleMapKind::Umbrella
};
let mut entry = X86ModuleMapEntry {
name: name.to_string(),
kind,
headers: Vec::new(),
umbrella_header: None,
submodules: Vec::new(),
explicit: kind == X86ModuleMapKind::Explicit,
system: lower.contains("system"),
exports: Vec::new(),
uses: Vec::new(),
link_libraries: Vec::new(),
};
if lower.contains("umbrella header ") {
let uh = if let Some(idx) = trimmed.find("umbrella header \"") {
let start = idx + 17;
if let Some(end) = trimmed[start..].find('"') {
Some(trimmed[start..start + end].to_string())
} else {
None
}
} else {
None
};
if let Some(ref uh_path) = uh {
umbrella_header = Some(base_dir.join(uh_path));
entry.umbrella_header = Some(base_dir.join(uh_path));
}
}
i += 1;
while i < lines.len() {
let bline = lines[i].trim();
if bline == "}" {
break;
}
self.parse_module_map_line(bline, &mut entry, &base_dir);
i += 1;
}
modules.push(entry);
}
i += 1;
}
Ok(X86ParsedModuleMap {
path: map_path.to_path_buf(),
modules,
is_framework,
framework_name,
has_umbrella_header: umbrella_header.is_some(),
umbrella_header,
has_submodules: false,
})
}
fn parse_module_map_line(&self, line: &str, entry: &mut X86ModuleMapEntry, base_dir: &Path) {
let trimmed = line.trim();
let lower = trimmed.to_lowercase();
if lower.starts_with("header ") {
let header = trimmed[7..].trim().trim_matches('"').trim();
if !header.is_empty() {
entry.headers.push(base_dir.join(header));
}
} else if lower.starts_with("umbrella header ") {
let header = trimmed[16..].trim().trim_matches('"').trim();
if !header.is_empty() {
entry.umbrella_header = Some(base_dir.join(header));
}
} else if lower.starts_with("export ") || lower.starts_with("export *") {
if let Some(rest) = trimmed.strip_prefix("export ") {
entry.exports.push(rest.trim().to_string());
}
} else if lower.starts_with("use ") {
entry.uses.push(trimmed[4..].trim().to_string());
} else if lower.starts_with("link ") {
let lib = trimmed[5..].trim().trim_matches('"').trim();
if !lib.is_empty() {
entry.link_libraries.push(lib.to_string());
}
} else if lower == "explicit" || lower == "explicit module" {
entry.explicit = true;
} else if lower == "system" {
entry.system = true;
}
}
}
pub struct X86ModuleCacheManager {
pub cache_root: PathBuf,
pub max_cache_size: u64,
pub max_versions: usize,
pub auto_cleanup: bool,
pub verify_integrity: bool,
memory_cache: HashMap<ModuleName, PathBuf>,
}
impl X86ModuleCacheManager {
pub fn new() -> Self {
let cache_root = PathBuf::from(X86_MODULE_CACHE_DIR);
Self {
cache_root,
max_cache_size: X86_CACHE_MAX_SIZE_BYTES,
max_versions: X86_CACHE_MAX_VERSIONS,
auto_cleanup: true,
verify_integrity: true,
memory_cache: HashMap::new(),
}
}
pub fn with_root(cache_root: PathBuf) -> Self {
Self {
cache_root,
max_cache_size: X86_CACHE_MAX_SIZE_BYTES,
max_versions: X86_CACHE_MAX_VERSIONS,
auto_cleanup: true,
verify_integrity: true,
memory_cache: HashMap::new(),
}
}
pub fn lookup(&mut self, module_name: &ModuleName) -> Option<PathBuf> {
if let Some(path) = self.memory_cache.get(module_name) {
if path.exists() {
return Some(path.clone());
}
}
let module_dir = self.cache_root.join(module_name.to_string());
if !module_dir.exists() {
return None;
}
let mut bmi_files: Vec<_> = fs::read_dir(&module_dir)
.ok()?
.filter_map(|e| e.ok())
.filter(|e| {
e.path().extension().map_or(false, |ext| {
ext == X86_BMI_EXTENSION.trim_start_matches('.')
})
})
.collect();
bmi_files.sort_by_key(|e| {
e.metadata()
.and_then(|m| m.modified())
.unwrap_or(UNIX_EPOCH)
});
bmi_files.reverse();
bmi_files.first().map(|e| {
let path = e.path();
self.memory_cache.insert(module_name.clone(), path.clone());
path
})
}
pub fn store(
&mut self,
module_name: &ModuleName,
bmi_path: &Path,
) -> Result<(), X86PCHModulesError> {
let module_dir = self.cache_root.join(module_name.to_string());
fs::create_dir_all(&module_dir)?;
let dest_path = module_dir.join(
bmi_path
.file_name()
.unwrap_or(std::ffi::OsStr::new("module.pcm")),
);
fs::copy(bmi_path, &dest_path)?;
self.memory_cache.insert(module_name.clone(), dest_path);
if self.auto_cleanup {
self.evict_old_versions(&module_dir);
}
if self.auto_cleanup {
self.enforce_size_limit()?;
}
Ok(())
}
pub fn invalidate(&mut self, module_name: &ModuleName) -> Result<(), X86PCHModulesError> {
self.memory_cache.remove(module_name);
let module_dir = self.cache_root.join(module_name.to_string());
if module_dir.exists() {
fs::remove_dir_all(&module_dir)?;
}
Ok(())
}
pub fn clear_cache(&mut self) -> Result<(), X86PCHModulesError> {
self.memory_cache.clear();
if self.cache_root.exists() {
fs::remove_dir_all(&self.cache_root)?;
fs::create_dir_all(&self.cache_root)?;
}
Ok(())
}
fn evict_old_versions(&self, module_dir: &Path) {
let mut bmi_files: Vec<_> = fs::read_dir(module_dir)
.into_iter()
.flatten()
.filter_map(|e| e.ok())
.filter(|e| {
e.path().extension().map_or(false, |ext| {
ext == X86_BMI_EXTENSION.trim_start_matches('.')
})
})
.collect();
if bmi_files.len() > self.max_versions {
bmi_files.sort_by_key(|e| {
e.metadata()
.and_then(|m| m.modified())
.unwrap_or(UNIX_EPOCH)
});
let to_remove = bmi_files.len() - self.max_versions;
for entry in bmi_files.iter().take(to_remove) {
let _ = fs::remove_file(entry.path());
}
}
}
fn enforce_size_limit(&self) -> Result<(), X86PCHModulesError> {
let total_size: u64 = self
.cache_root
.exists()
.then(|| walkdir_size(&self.cache_root).unwrap_or(0))
.unwrap_or(0);
if total_size > self.max_cache_size {
let mut entries: Vec<_> = fs::read_dir(&self.cache_root)?
.filter_map(|e| e.ok())
.collect();
entries.sort_by_key(|e| {
e.metadata()
.and_then(|m| m.modified())
.unwrap_or(UNIX_EPOCH)
});
let mut remaining = total_size;
for entry in &entries {
if remaining <= self.max_cache_size {
break;
}
if let Ok(meta) = entry.metadata() {
if entry.path().is_dir() {
if let Ok(dir_size) = walkdir_size(&entry.path()) {
remaining -= dir_size;
}
}
let _ = fs::remove_dir_all(entry.path());
}
}
}
Ok(())
}
}
fn walkdir_size(path: &Path) -> io::Result<u64> {
let mut total = 0u64;
if path.is_dir() {
for entry in fs::read_dir(path)? {
let entry = entry?;
let p = entry.path();
if p.is_dir() {
total += walkdir_size(&p)?;
} else if p.is_file() {
total += entry.metadata()?.len();
}
}
} else if path.is_file() {
total += fs::metadata(path)?.len();
}
Ok(total)
}
#[derive(Debug, Clone)]
pub struct X86DependencyScanResult {
pub source_path: PathBuf,
pub header_dependencies: Vec<PathBuf>,
pub module_dependencies: Vec<ModuleName>,
pub transitive_imports: Vec<ModuleName>,
pub all_dependencies: Vec<ModuleName>,
pub success: bool,
pub scanned_at: SystemTime,
}
pub struct X86ModuleDependencyScanner {
pub include_paths: Vec<PathBuf>,
pub resolve_transitive: bool,
pub max_depth: u32,
pub scan_headers: bool,
}
impl X86ModuleDependencyScanner {
pub fn new() -> Self {
Self {
include_paths: vec![
PathBuf::from("/usr/include"),
PathBuf::from("/usr/local/include"),
],
resolve_transitive: true,
max_depth: 8,
scan_headers: true,
}
}
pub fn scan(&self, source_path: &Path) -> Result<X86DependencyScanResult, X86PCHModulesError> {
let content = fs::read_to_string(source_path)?;
let mut header_deps = Vec::new();
let mut module_deps = Vec::new();
let mut transitive = Vec::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("#include ") {
let include = if let Some(q) = trimmed.find('"') {
let rest = &trimmed[q + 1..];
rest.find('"').map(|e| rest[..e].to_string())
} else if let Some(l) = trimmed.find('<') {
let rest = &trimmed[l + 1..];
rest.find('>').map(|e| rest[..e].to_string())
} else {
None
};
if let Some(path) = include {
header_deps.push(PathBuf::from(&path));
}
}
if trimmed.starts_with("import ") && !trimmed.starts_with("import <") {
if let Some(rest) = trimmed.strip_prefix("import ") {
let name = rest.trim_end_matches(';').trim();
if !name.is_empty() && !name.contains('<') {
let module_name = ModuleName::from_str(name);
module_deps.push(module_name.clone());
if self.resolve_transitive {
transitive.push(module_name);
}
}
}
}
}
let mut all_deps = module_deps.clone();
all_deps.extend(transitive.clone());
all_deps.sort_by(|a, b| a.to_string().cmp(&b.to_string()));
all_deps.dedup_by(|a, b| a.to_string() == b.to_string());
Ok(X86DependencyScanResult {
source_path: source_path.to_path_buf(),
header_dependencies: header_deps,
module_dependencies: module_deps,
transitive_imports: transitive,
all_dependencies: all_deps,
success: true,
scanned_at: SystemTime::now(),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ModuleVisibility {
Public,
Private,
Reexportable,
}
impl fmt::Display for X86ModuleVisibility {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Public => write!(f, "public"),
Self::Private => write!(f, "private"),
Self::Reexportable => write!(f, "reexportable"),
}
}
}
pub struct X86ModuleVisibilityManager {
visibilities: HashMap<ModuleName, X86ModuleVisibility>,
default_visibility: X86ModuleVisibility,
}
impl X86ModuleVisibilityManager {
pub fn new() -> Self {
Self {
visibilities: HashMap::new(),
default_visibility: X86ModuleVisibility::Public,
}
}
pub fn set_visibility(&mut self, module_name: &ModuleName, visibility: X86ModuleVisibility) {
self.visibilities.insert(module_name.clone(), visibility);
}
pub fn get_visibility(&self, module_name: &ModuleName) -> X86ModuleVisibility {
self.visibilities
.get(module_name)
.copied()
.unwrap_or(self.default_visibility)
}
pub fn is_visible(&self, module_name: &ModuleName, importing_module: &ModuleName) -> bool {
let vis = self.get_visibility(module_name);
match vis {
X86ModuleVisibility::Public => true,
X86ModuleVisibility::Reexportable => true,
X86ModuleVisibility::Private => {
false }
}
}
pub fn set_default_visibility(&mut self, visibility: X86ModuleVisibility) {
self.default_visibility = visibility;
}
pub fn remove_visibility(&mut self, module_name: &ModuleName) {
self.visibilities.remove(module_name);
}
pub fn clear(&mut self) {
self.visibilities.clear();
}
}
#[derive(Debug, Clone)]
pub struct X86HeaderModule {
pub module_name: ModuleName,
pub header_path: PathBuf,
pub bmi_path: PathBuf,
pub module_map_entry: X86ModuleMapEntry,
pub is_system: bool,
pub content_hash: [u8; 32],
pub is_valid: bool,
}
pub struct X86HeaderModules {
pub header_mappings: HashMap<String, ModuleName>,
pub system_include_paths: Vec<PathBuf>,
pub auto_module_maps: bool,
}
impl X86HeaderModules {
pub fn new() -> Self {
let mut mappings = HashMap::new();
let std_headers = [
("stdio.h", "std.stdio"),
("stdlib.h", "std.stdlib"),
("string.h", "std.string"),
("math.h", "std.math"),
("time.h", "std.time"),
("assert.h", "std.assert"),
("ctype.h", "std.ctype"),
("errno.h", "std.errno"),
("float.h", "std.float"),
("limits.h", "std.limits"),
("locale.h", "std.locale"),
("setjmp.h", "std.setjmp"),
("signal.h", "std.signal"),
("stdarg.h", "std.stdarg"),
("stddef.h", "std.stddef"),
("stdint.h", "std.stdint"),
("stdio.h", "std.stdio"),
("stdlib.h", "std.stdlib"),
("string.h", "std.string"),
("inttypes.h", "std.inttypes"),
("stdbool.h", "std.stdbool"),
("stdalign.h", "std.stdalign"),
("stdnoreturn.h", "std.stdnoreturn"),
("threads.h", "std.threads"),
("uchar.h", "std.uchar"),
("wchar.h", "std.wchar"),
("wctype.h", "std.wctype"),
("complex.h", "std.complex"),
("fenv.h", "std.fenv"),
("tgmath.h", "std.tgmath"),
];
let cpp_headers = [
("vector", "std.vector"),
("string", "std.string_view"),
("map", "std.map"),
("set", "std.set"),
("algorithm", "std.algorithm"),
("iostream", "std.iostream"),
("memory", "std.memory"),
("utility", "std.utility"),
("functional", "std.functional"),
("iterator", "std.iterator"),
("numeric", "std.numeric"),
("array", "std.array"),
("deque", "std.deque"),
("list", "std.list"),
("forward_list", "std.forward_list"),
("queue", "std.queue"),
("stack", "std.stack"),
("unordered_map", "std.unordered_map"),
("unordered_set", "std.unordered_set"),
("bitset", "std.bitset"),
("tuple", "std.tuple"),
("optional", "std.optional"),
("variant", "std.variant"),
("any", "std.any"),
("string_view", "std.string_view"),
("span", "std.span"),
("regex", "std.regex"),
("atomic", "std.atomic"),
("thread", "std.thread"),
("mutex", "std.mutex"),
("condition_variable", "std.condition_variable"),
("future", "std.future"),
("chrono", "std.chrono"),
("filesystem", "std.filesystem"),
("random", "std.random"),
("ratio", "std.ratio"),
("complex", "std.complex"),
("valarray", "std.valarray"),
("type_traits", "std.type_traits"),
("typeindex", "std.typeindex"),
("typeinfo", "std.typeinfo"),
("exception", "std.exception"),
("stdexcept", "std.stdexcept"),
("new", "std.new"),
("initializer_list", "std.initializer_list"),
("scoped_allocator", "std.scoped_allocator"),
("memory_resource", "std.memory_resource"),
("compare", "std.compare"),
("concepts", "std.concepts"),
("coroutine", "std.coroutine"),
("source_location", "std.source_location"),
("version", "std.version"),
("format", "std.format"),
("ranges", "std.ranges"),
("barrier", "std.barrier"),
("latch", "std.latch"),
("semaphore", "std.semaphore"),
("syncstream", "std.syncstream"),
("stop_token", "std.stop_token"),
("charconv", "std.charconv"),
("expected", "std.expected"),
("bit", "std.bit"),
];
for (header, module) in std_headers.iter() {
mappings.insert(header.to_string(), ModuleName::from_str(module));
}
for (header, module) in cpp_headers.iter() {
mappings.insert(header.to_string(), ModuleName::from_str(module));
}
Self {
header_mappings: mappings,
system_include_paths: vec![
PathBuf::from("/usr/include"),
PathBuf::from("/usr/local/include"),
PathBuf::from("/usr/include/c++/v1"),
PathBuf::from("/usr/include/x86_64-linux-gnu"),
],
auto_module_maps: true,
}
}
pub fn process(&self, header_path: &Path) -> Result<X86HeaderModule, X86PCHModulesError> {
let header_name = header_path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let module_name = self
.header_mappings
.get(&header_name)
.cloned()
.unwrap_or_else(|| {
let name = header_name.replace('.', "_");
ModuleName::from_str(&format!("header.{}", name))
});
let is_system = self
.system_include_paths
.iter()
.any(|p| header_path.starts_with(p));
let content_hash = if let Ok(content) = fs::read(&header_path) {
let mut hash = [0u8; 32];
let mut state: u64 = 0x6a09e667f3bcc908;
for (i, &byte) in content.iter().enumerate() {
state = state.wrapping_mul(0x9e3779b97f4a7c15);
state = state.wrapping_add(byte as u64);
state = state.wrapping_add((i as u64).wrapping_mul(0xbf58476d1ce4e5b9));
hash[(i % 32) as usize] ^= (state >> ((i % 8) * 8)) as u8;
}
hash[0..8].copy_from_slice(&state.to_le_bytes());
hash
} else {
[0u8; 32]
};
let bmi_path = PathBuf::from(X86_MODULE_CACHE_DIR)
.join(X86_MODULE_BMI_SUBDIR)
.join(format!("{}{}", module_name, X86_BMI_EXTENSION));
Ok(X86HeaderModule {
module_name,
header_path: header_path.to_path_buf(),
bmi_path,
module_map_entry: X86ModuleMapEntry {
name: header_name,
kind: if is_system {
X86ModuleMapKind::Explicit
} else {
X86ModuleMapKind::Umbrella
},
headers: vec![header_path.to_path_buf()],
umbrella_header: None,
submodules: Vec::new(),
explicit: true,
system: is_system,
exports: Vec::new(),
uses: Vec::new(),
link_libraries: Vec::new(),
},
is_system,
content_hash,
is_valid: true,
})
}
pub fn resolve_header_module(&self, header_name: &str) -> Option<&ModuleName> {
self.header_mappings.get(header_name)
}
}
#[derive(Debug, Clone)]
pub struct X86FrameworkModule {
pub framework_name: String,
pub framework_path: PathBuf,
pub module_map: X86ParsedModuleMap,
pub headers_dir: PathBuf,
pub modules_dir: PathBuf,
pub is_private: bool,
}
pub struct X86FrameworkModules {
pub framework_search_paths: Vec<PathBuf>,
pub framework_cache: HashMap<String, X86FrameworkModule>,
pub auto_discover: bool,
}
impl X86FrameworkModules {
pub fn new() -> Self {
Self {
framework_search_paths: vec![
PathBuf::from("/System/Library/Frameworks"),
PathBuf::from("/Library/Frameworks"),
],
framework_cache: HashMap::new(),
auto_discover: true,
}
}
pub fn process(
&mut self,
framework_path: &Path,
) -> Result<X86FrameworkModule, X86PCHModulesError> {
let framework_name = framework_path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let module_map_path = framework_path.join("Modules").join("module.modulemap");
let module_map = if module_map_path.exists() {
let parser = X86ModuleMapParser {
resolve_paths: true,
base_directory: Some(framework_path.to_path_buf()),
validate_headers: false,
};
parser.parse(&module_map_path)?
} else {
X86ParsedModuleMap {
path: framework_path.to_path_buf(),
modules: vec![X86ModuleMapEntry {
name: framework_name.clone(),
kind: X86ModuleMapKind::Framework,
headers: self.find_framework_headers(framework_path),
umbrella_header: None,
submodules: Vec::new(),
explicit: true,
system: true,
exports: Vec::new(),
uses: Vec::new(),
link_libraries: vec![framework_name.clone()],
}],
is_framework: true,
framework_name: Some(framework_name.clone()),
has_umbrella_header: false,
umbrella_header: None,
has_submodules: false,
}
};
let headers_dir = framework_path.join("Headers");
let modules_dir = framework_path.join("Modules");
let framework_module = X86FrameworkModule {
framework_name: framework_name.clone(),
framework_path: framework_path.to_path_buf(),
module_map,
headers_dir,
modules_dir,
is_private: framework_path.parent().map_or(false, |p| {
p.file_name().map_or(false, |n| n == "PrivateFrameworks")
}),
};
self.framework_cache
.insert(framework_name, framework_module.clone());
Ok(framework_module)
}
fn find_framework_headers(&self, framework_path: &Path) -> Vec<PathBuf> {
let headers_dir = framework_path.join("Headers");
if !headers_dir.exists() {
return Vec::new();
}
let mut headers = Vec::new();
if let Ok(entries) = fs::read_dir(&headers_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map_or(false, |e| e == "h" || e == "hpp") {
headers.push(path);
}
}
}
headers
}
pub fn resolve_framework(
&mut self,
framework_name: &str,
) -> Result<Option<X86FrameworkModule>, X86PCHModulesError> {
if let Some(cached) = self.framework_cache.get(framework_name) {
return Ok(Some(cached.clone()));
}
for search_path in &self.framework_search_paths {
let framework_dir = search_path.join(format!("{}.framework", framework_name));
if framework_dir.exists() {
return self.process(&framework_dir).map(Some);
}
}
Ok(None)
}
}
pub struct X86ModuleMapLanguage {
pub parse_umbrella_dirs: bool,
pub resolve_wildcards: bool,
pub parse_config_macros: bool,
pub validate_requires: bool,
}
#[derive(Debug, Clone)]
pub struct X86ModuleMapDocument {
pub path: PathBuf,
pub modules: Vec<X86ModuleMapModule>,
pub extern_modules: Vec<String>,
pub is_framework: bool,
pub config_macros: Vec<String>,
pub requires: Vec<String>,
pub conflicts: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86ModuleMapModule {
pub name: String,
pub is_framework: bool,
pub is_explicit: bool,
pub is_system: bool,
pub is_exhaustive: bool,
pub umbrella_header: Option<PathBuf>,
pub umbrella_directory: Option<PathBuf>,
pub headers: Vec<X86ModuleMapHeader>,
pub submodules: Vec<X86ModuleMapModule>,
pub exports: Vec<String>,
pub uses: Vec<String>,
pub link_libraries: Vec<String>,
pub config_macros: Vec<String>,
pub conflicts: Vec<String>,
pub requires: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86ModuleMapHeader {
pub path: String,
pub is_private: bool,
pub is_textual: bool,
pub is_umbrella: bool,
pub has_wildcard: bool,
pub size_constraint: Option<u64>,
pub mtime_constraint: Option<SystemTime>,
}
impl X86ModuleMapLanguage {
pub fn new() -> Self {
Self {
parse_umbrella_dirs: true,
resolve_wildcards: true,
parse_config_macros: true,
validate_requires: false,
}
}
pub fn parse(&self, map_path: &Path) -> Result<X86ModuleMapDocument, X86PCHModulesError> {
let content = fs::read_to_string(map_path)?;
let mut doc = X86ModuleMapDocument {
path: map_path.to_path_buf(),
modules: Vec::new(),
extern_modules: Vec::new(),
is_framework: false,
config_macros: Vec::new(),
requires: Vec::new(),
conflicts: Vec::new(),
};
let base_dir = map_path.parent().unwrap_or(Path::new("."));
let tokens = self.tokenize_module_map(&content);
let mut pos = 0;
while pos < tokens.len() {
match tokens[pos].as_str() {
"framework" => {
if pos + 2 < tokens.len() && tokens[pos + 1] == "module" {
let name = tokens[pos + 2].clone();
pos += 3;
let module =
self.parse_module_body(&tokens, &mut pos, &name, true, base_dir)?;
doc.modules.push(module);
doc.is_framework = true;
} else {
pos += 1;
}
}
"module" => {
if pos + 1 < tokens.len() {
let name = tokens[pos + 1].clone();
pos += 2;
let module =
self.parse_module_body(&tokens, &mut pos, &name, false, base_dir)?;
doc.modules.push(module);
} else {
pos += 1;
}
}
"extern" => {
if pos + 2 < tokens.len() && tokens[pos + 1] == "module" {
doc.extern_modules.push(tokens[pos + 2].clone());
pos += 3;
} else {
pos += 1;
}
}
"config_macros" => {
pos += 1;
while pos < tokens.len() && tokens[pos] != "}" {
let m = tokens[pos].trim_end_matches(',');
if !m.is_empty() {
doc.config_macros.push(m.to_string());
}
pos += 1;
}
if pos < tokens.len() {
pos += 1; }
}
"requires" => {
pos += 1;
let mut req_parts = Vec::new();
while pos < tokens.len() && tokens[pos] != ";" {
req_parts.push(tokens[pos].clone());
pos += 1;
}
if pos < tokens.len() {
pos += 1; }
doc.requires.push(req_parts.join(" "));
}
_ => {
pos += 1;
}
}
}
Ok(doc)
}
fn tokenize_module_map(&self, content: &str) -> Vec<String> {
let mut tokens = Vec::new();
let chars: Vec<char> = content.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i].is_whitespace() {
i += 1;
continue;
}
if chars[i] == '/' && i + 1 < chars.len() && chars[i + 1] == '/' {
while i < chars.len() && chars[i] != '\n' {
i += 1;
}
continue;
}
if chars[i] == '"' {
let mut s = String::new();
i += 1;
while i < chars.len() && chars[i] != '"' {
if chars[i] == '\\' && i + 1 < chars.len() {
i += 1;
}
s.push(chars[i]);
i += 1;
}
if i < chars.len() {
i += 1; }
tokens.push(s);
continue;
}
if "{};,*".contains(chars[i]) {
tokens.push(chars[i].to_string());
i += 1;
continue;
}
if chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == '.' {
let mut s = String::new();
while i < chars.len()
&& (chars[i].is_alphanumeric()
|| chars[i] == '_'
|| chars[i] == '.'
|| chars[i] == '-'
|| chars[i] == '/')
{
s.push(chars[i]);
i += 1;
}
tokens.push(s);
continue;
}
i += 1;
}
tokens
}
fn parse_module_body(
&self,
tokens: &[String],
pos: &mut usize,
name: &str,
is_framework: bool,
base_dir: &Path,
) -> Result<X86ModuleMapModule, X86PCHModulesError> {
let mut module = X86ModuleMapModule {
name: name.to_string(),
is_framework,
is_explicit: false,
is_system: false,
is_exhaustive: false,
umbrella_header: None,
umbrella_directory: None,
headers: Vec::new(),
submodules: Vec::new(),
exports: Vec::new(),
uses: Vec::new(),
link_libraries: Vec::new(),
config_macros: Vec::new(),
conflicts: Vec::new(),
requires: Vec::new(),
};
if *pos < tokens.len() && tokens[*pos] == "{" {
*pos += 1;
}
let mut depth = 1;
while *pos < tokens.len() && depth > 0 {
let token = &tokens[*pos];
match token.as_str() {
"}" => {
depth -= 1;
*pos += 1;
if depth == 0 {
break;
}
}
"{" => {
depth += 1;
*pos += 1;
}
"explicit" => {
module.is_explicit = true;
*pos += 1;
}
"system" => {
module.is_system = true;
*pos += 1;
}
"exhaustive" => {
module.is_exhaustive = true;
*pos += 1;
}
"umbrella" => {
*pos += 1;
if *pos < tokens.len() {
match tokens[*pos].as_str() {
"header" => {
*pos += 1;
if *pos < tokens.len() {
module.umbrella_header = Some(base_dir.join(&tokens[*pos]));
*pos += 1;
}
}
"directory" => {
*pos += 1;
if *pos < tokens.len() {
module.umbrella_directory = Some(base_dir.join(&tokens[*pos]));
*pos += 1;
}
}
_ => {}
}
}
}
"header" => {
*pos += 1;
if *pos < tokens.len() {
let header_path = tokens[*pos].clone();
let has_wildcard = header_path.contains('*');
module.headers.push(X86ModuleMapHeader {
path: base_dir.join(&header_path).to_string_lossy().to_string(),
is_private: false,
is_textual: false,
is_umbrella: false,
has_wildcard,
size_constraint: None,
mtime_constraint: None,
});
*pos += 1;
}
}
"private" => {
*pos += 1;
if *pos < tokens.len() && tokens[*pos] == "header" {
*pos += 1;
if *pos < tokens.len() {
module.headers.push(X86ModuleMapHeader {
path: base_dir.join(&tokens[*pos]).to_string_lossy().to_string(),
is_private: true,
is_textual: false,
is_umbrella: false,
has_wildcard: false,
size_constraint: None,
mtime_constraint: None,
});
*pos += 1;
}
}
}
"textual" => {
*pos += 1;
if *pos < tokens.len() && tokens[*pos] == "header" {
*pos += 1;
if *pos < tokens.len() {
module.headers.push(X86ModuleMapHeader {
path: base_dir.join(&tokens[*pos]).to_string_lossy().to_string(),
is_private: false,
is_textual: true,
is_umbrella: false,
has_wildcard: false,
size_constraint: None,
mtime_constraint: None,
});
*pos += 1;
}
}
}
"export" => {
*pos += 1;
if *pos < tokens.len() && tokens[*pos] == "*" {
module.exports.push("*".to_string());
*pos += 1;
} else {
while *pos < tokens.len() && tokens[*pos] != ";" && tokens[*pos] != "}" {
let e = tokens[*pos].trim_end_matches(',');
if !e.is_empty() {
module.exports.push(e.to_string());
}
*pos += 1;
}
}
}
"use" => {
*pos += 1;
if *pos < tokens.len() {
module.uses.push(tokens[*pos].clone());
*pos += 1;
}
}
"link" => {
*pos += 1;
if *pos < tokens.len() {
module.link_libraries.push(tokens[*pos].clone());
*pos += 1;
}
}
"config_macros" => {
*pos += 1;
while *pos < tokens.len() && tokens[*pos] != ";" && tokens[*pos] != "}" {
module.config_macros.push(tokens[*pos].clone());
*pos += 1;
}
}
"conflict" => {
*pos += 1;
if *pos < tokens.len() {
module.conflicts.push(tokens[*pos].clone());
*pos += 1;
}
}
"requires" => {
*pos += 1;
let mut req_parts = Vec::new();
while *pos < tokens.len() && tokens[*pos] != ";" && tokens[*pos] != "}" {
req_parts.push(tokens[*pos].clone());
*pos += 1;
}
module.requires.push(req_parts.join(" "));
}
"module" | "framework"
if *pos + 2 < tokens.len() && tokens[*pos + 1] != "module" =>
{
let sub_framework = tokens[*pos] == "framework";
*pos += if sub_framework { 2 } else { 1 };
if *pos < tokens.len() {
let sub_name = tokens[*pos].clone();
*pos += 1;
let submodule = self.parse_module_body(
tokens,
pos,
&sub_name,
sub_framework,
base_dir,
)?;
module.submodules.push(submodule);
}
}
_ => {
*pos += 1;
}
}
}
Ok(module)
}
}
pub struct X86PCHBMIFormat {
pub pch_version: (u32, u32),
pub bmi_version: u32,
pub include_locations: bool,
pub include_macros: bool,
pub include_identifiers: bool,
pub include_input_files: bool,
}
impl X86PCHBMIFormat {
pub fn new() -> Self {
Self {
pch_version: (X86_PCH_VERSION_MAJOR, X86_PCH_VERSION_MINOR),
bmi_version: X86_BMI_EXTENDED_VERSION,
include_locations: true,
include_macros: true,
include_identifiers: true,
include_input_files: true,
}
}
pub fn block_magic(block_type: &str) -> u32 {
match block_type {
"ast" => X86_BMI_BLOCK_AST,
"identifier" => X86_BMI_BLOCK_IDENTIFIER,
"source_manager" => X86_BMI_BLOCK_SOURCE_MANAGER,
"preprocessor" => X86_BMI_BLOCK_PREPROCESSOR,
"control" => X86_BMI_BLOCK_CONTROL,
"input_files" => X86_BMI_BLOCK_INPUT_FILES,
"module_map" => X86_BMI_BLOCK_MODULE_MAP,
"module_dependencies" => X86_BMI_BLOCK_MODULE_DEPENDENCIES,
"chain_metadata" => X86_PCH_BLOCK_CHAIN_METADATA,
_ => 0,
}
}
pub fn describe(&self) -> String {
format!(
"PCH v{}.{}, BMI v{}, locations={}, macros={}, identifiers={}, input_files={}",
self.pch_version.0,
self.pch_version.1,
self.bmi_version,
self.include_locations,
self.include_macros,
self.include_identifiers,
self.include_input_files
)
}
}
#[derive(Debug, Clone)]
pub struct X86ASTBlock {
pub magic: u32,
pub block_size: u64,
pub num_types: u32,
pub num_declarations: u32,
pub num_statements: u32,
pub num_expressions: u32,
pub type_data: Vec<u8>,
pub declaration_data: Vec<u8>,
pub statement_data: Vec<u8>,
pub expression_data: Vec<u8>,
}
impl X86ASTBlock {
pub fn new() -> Self {
Self {
magic: X86_BMI_BLOCK_AST,
block_size: 0,
num_types: 0,
num_declarations: 0,
num_statements: 0,
num_expressions: 0,
type_data: Vec::new(),
declaration_data: Vec::new(),
statement_data: Vec::new(),
expression_data: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86IdentifierBlock {
pub magic: u32,
pub num_identifiers: u32,
pub identifiers: HashMap<String, u32>,
}
impl X86IdentifierBlock {
pub fn new() -> Self {
Self {
magic: X86_BMI_BLOCK_IDENTIFIER,
num_identifiers: 0,
identifiers: HashMap::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SourceManagerBlock {
pub magic: u32,
pub num_files: u32,
pub files: Vec<X86SourceFileEntry>,
}
impl X86SourceManagerBlock {
pub fn new() -> Self {
Self {
magic: X86_BMI_BLOCK_SOURCE_MANAGER,
num_files: 0,
files: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86PreprocessorBlock {
pub magic: u32,
pub num_macros: u32,
pub macros: HashMap<String, MacroDefinitionData>,
pub conditional_depth: u32,
pub is_complete: bool,
}
impl X86PreprocessorBlock {
pub fn new() -> Self {
Self {
magic: X86_BMI_BLOCK_PREPROCESSOR,
num_macros: 0,
macros: HashMap::new(),
conditional_depth: 0,
is_complete: true,
}
}
}
#[derive(Debug, Clone)]
pub struct X86ControlBlock {
pub magic: u32,
pub metadata: HashMap<String, String>,
pub language_options: Option<ClangOptions>,
pub diagnostic_options: X86DiagnosticOptions,
pub format_version: (u32, u32),
pub clang_version: String,
}
#[derive(Debug, Clone)]
pub struct X86DiagnosticOptions {
pub warnings_enabled: bool,
pub pedantic: bool,
pub wall: bool,
pub werror: bool,
pub verbose: bool,
}
impl Default for X86DiagnosticOptions {
fn default() -> Self {
Self {
warnings_enabled: true,
pedantic: false,
wall: false,
werror: false,
verbose: false,
}
}
}
impl X86ControlBlock {
pub fn new() -> Self {
Self {
magic: X86_BMI_BLOCK_CONTROL,
metadata: HashMap::new(),
language_options: None,
diagnostic_options: X86DiagnosticOptions::default(),
format_version: (X86_PCH_VERSION_MAJOR, X86_PCH_VERSION_MINOR),
clang_version: "17.0.0 (llvm-native-core)".to_string(),
}
}
}
pub struct X86InputFileValidator {
pub stat_cache: X86PCHStatCache,
pub use_content_hash: bool,
pub check_size: bool,
pub check_mtime: bool,
pub max_staleness: Option<Duration>,
}
impl X86InputFileValidator {
pub fn new() -> Self {
Self {
stat_cache: X86PCHStatCache::new(),
use_content_hash: true,
check_size: true,
check_mtime: true,
max_staleness: Some(Duration::from_secs(3600)),
}
}
pub fn validate_file(
&mut self,
file_path: &Path,
expected_size: Option<u64>,
expected_mtime: Option<SystemTime>,
expected_hash: Option<&[u8; 32]>,
) -> Result<bool, X86PCHModulesError> {
if let Some(cached) = self.stat_cache.lookup(file_path) {
return Ok(cached.is_valid);
}
let metadata = match fs::metadata(file_path) {
Ok(m) => m,
Err(e) => {
self.stat_cache.insert(
file_path,
X86CachedPCHValidation {
is_valid: false,
failure_reason: Some(format!("Cannot stat: {}", e)),
validated_at: SystemTime::now(),
},
);
return Ok(false);
}
};
if self.check_size {
if let Some(expected) = expected_size {
if metadata.len() != expected {
self.stat_cache.insert(
file_path,
X86CachedPCHValidation {
is_valid: false,
failure_reason: Some(format!(
"Size mismatch: expected {}, got {}",
expected,
metadata.len()
)),
validated_at: SystemTime::now(),
},
);
return Ok(false);
}
}
}
if self.check_mtime {
if let Some(expected) = expected_mtime {
let actual_mtime = metadata.modified().unwrap_or(UNIX_EPOCH);
if actual_mtime > expected {
self.stat_cache.insert(
file_path,
X86CachedPCHValidation {
is_valid: false,
failure_reason: Some("File modified after PCH generation".to_string()),
validated_at: SystemTime::now(),
},
);
return Ok(false);
}
}
}
if self.use_content_hash {
if let Some(expected) = expected_hash {
if let Ok(content) = fs::read(file_path) {
let actual_hash = Self::compute_content_hash(&content);
if actual_hash != *expected {
self.stat_cache.insert(
file_path,
X86CachedPCHValidation {
is_valid: false,
failure_reason: Some("Content hash mismatch".to_string()),
validated_at: SystemTime::now(),
},
);
return Ok(false);
}
}
}
}
self.stat_cache.insert(
file_path,
X86CachedPCHValidation {
is_valid: true,
failure_reason: None,
validated_at: SystemTime::now(),
},
);
Ok(true)
}
pub fn validate_all_files(
&mut self,
files: &[X86SourceFileEntry],
) -> Result<Vec<(PathBuf, bool)>, X86PCHModulesError> {
let mut results = Vec::new();
for entry in files {
let valid = self.validate_file(
&entry.path,
if self.check_size {
Some(entry.size)
} else {
None
},
if self.check_mtime {
Some(entry.mtime)
} else {
None
},
if self.use_content_hash {
Some(&entry.content_hash)
} else {
None
},
)?;
results.push((entry.path.clone(), valid));
}
Ok(results)
}
pub fn invalidate_all(&mut self) {
self.stat_cache.clear();
}
pub fn compute_content_hash(data: &[u8]) -> [u8; 32] {
let mut hash = [0u8; 32];
let mut state: u64 = 0x6a09e667f3bcc908;
for (i, &byte) in data.iter().enumerate() {
state = state.wrapping_mul(0x9e3779b97f4a7c15);
state = state.wrapping_add(byte as u64);
state = state.wrapping_add((i as u64).wrapping_mul(0xbf58476d1ce4e5b9));
hash[(i % 32) as usize] ^= (state >> ((i % 8) * 8)) as u8;
}
hash[0..8].copy_from_slice(&state.to_le_bytes());
hash
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::io::Write;
fn test_options() -> ClangOptions {
ClangOptions {
standard: CLangStandard::C17,
optimize: true,
debug_info: false,
warnings: true,
pedantic: false,
wall: true,
werror: false,
verbose: false,
includes: vec![],
defines: vec![],
output_file: None,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
}
}
fn tmp_dir() -> PathBuf {
let dir = std::env::temp_dir().join("clang_pch_test");
let _ = fs::create_dir_all(&dir);
dir
}
#[test]
fn test_x86_pch_modules_new() {
let tm = X86TargetMachine::default();
let opts = test_options();
let pch_mod = X86PCHModules::new(tm, opts);
assert!(pch_mod.pch_enabled);
assert!(pch_mod.modules_enabled);
assert!(!pch_mod.framework_modules_enabled);
assert!(pch_mod.header_modules_enabled);
assert!(pch_mod.pch_chain.is_empty());
assert!(pch_mod.loaded_modules.is_empty());
assert_eq!(pch_mod.stats.pch_generated, 0);
}
#[test]
fn test_enable_disable_features() {
let tm = X86TargetMachine::default();
let opts = test_options();
let mut pch_mod = X86PCHModules::new(tm, opts);
pch_mod.set_pch_enabled(false);
assert!(!pch_mod.pch_enabled);
pch_mod.set_modules_enabled(false);
assert!(!pch_mod.modules_enabled);
pch_mod.set_framework_modules_enabled(true);
assert!(pch_mod.framework_modules_enabled);
pch_mod.set_header_modules_enabled(false);
assert!(!pch_mod.header_modules_enabled);
}
#[test]
fn test_generate_pch_basic() {
let tmp = tmp_dir();
let header_path = tmp.join("test_header.h");
let pch_path = tmp.join("test_header.pch");
let header_content = r#"
#define VERSION 1
#define MAX_SIZE 1024
static inline int add(int a, int b) {
return a + b;
}
const char* get_version(void) {
return "1.0.0";
}
"#;
fs::write(&header_path, header_content).unwrap();
let tm = X86TargetMachine::default();
let opts = test_options();
let mut pch_mod = X86PCHModules::new(tm, opts);
let result = pch_mod.generate_pch(&header_path, &pch_path, &test_options());
assert!(result.is_ok(), "PCH generation failed: {:?}", result.err());
let pch_file = result.unwrap();
assert_eq!(pch_file.kind, X86PCHKind::Project);
assert_eq!(pch_file.chain_index, 0);
assert!(pch_file.header.file_size > 0);
assert_eq!(&pch_file.header.magic, X86_PCH_MAGIC);
let metadata = fs::metadata(&pch_path).unwrap();
assert!(metadata.len() > 0);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_generator_identifier_table() {
let mut r#gen = X86PCHGenerator::new();
let content = "int x; float y; double z; struct S { int a; };";
r#gen.build_identifier_table(content);
assert!(!r#gen.identifier_table.is_empty());
assert!(r#gen.identifier_table.contains_key("int"));
assert!(r#gen.identifier_table.contains_key("float"));
assert!(r#gen.identifier_table.contains_key("double"));
assert!(r#gen.identifier_table.contains_key("struct"));
}
#[test]
fn test_pch_generator_macro_definitions() {
let mut r#gen = X86PCHGenerator::new();
let content = "#define VERSION 1\n#define MAX(a,b) ((a)>(b)?(a):(b))\n#define PI 3.14159";
r#gen.build_macro_definitions(content);
assert_eq!(r#gen.macro_definitions.len(), 3);
assert!(r#gen.macro_definitions.contains_key("VERSION"));
assert!(r#gen.macro_definitions.contains_key("MAX"));
assert!(r#gen.macro_definitions.contains_key("PI"));
let max_def = r#gen.macro_definitions.get("MAX").unwrap();
assert!(max_def.is_function_like);
assert_eq!(max_def.parameters, vec!["a", "b"]);
}
#[test]
fn test_pch_generator_source_files() {
let mut r#gen = X86PCHGenerator::new();
let tmp = tmp_dir();
let path = tmp.join("test.h");
fs::write(&path, "test content line1\nline2\nline3\n").unwrap();
r#gen.build_source_files(&path, "test content line1\nline2\nline3\n");
assert_eq!(r#gen.source_files.len(), 1);
let entry = &r#gen.source_files[0];
assert!(entry.line_table.len() > 0);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_generate_pch_with_macros() {
let tmp = tmp_dir();
let header_path = tmp.join("macro_header.h");
let pch_path = tmp.join("macro_header.pch");
fs::write(
&header_path,
"#define FOO 42\n#define BAR(x) ((x)*2)\n#define BAZ \"hello\"\n",
)
.unwrap();
let tm = X86TargetMachine::default();
let mut pch_mod = X86PCHModules::new(tm, test_options());
let result = pch_mod.generate_pch(&header_path, &pch_path, &test_options());
assert!(result.is_ok());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_validator_basic() {
let validator = X86PCHValidator::new();
assert!(validator.use_stat_cache);
assert!(validator.validate_input_hashes);
let mut stat_cache = X86PCHStatCache::new();
let tmp = tmp_dir();
let pch_path = tmp.join("nonexistent.pch");
let result = validator.validate(&pch_path, &test_options(), &mut stat_cache);
assert!(result.is_err());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_validator_version_check() {
let tmp = tmp_dir();
let pch_path = tmp.join("bad_version.pch");
{
let mut f = File::create(&pch_path).unwrap();
f.write_all(X86_PCH_MAGIC).unwrap();
f.write_all(&999u32.to_le_bytes()).unwrap(); f.write_all(&0u32.to_le_bytes()).unwrap();
}
let validator = X86PCHValidator::new();
let mut stat_cache = X86PCHStatCache::new();
let result = validator.validate(&pch_path, &test_options(), &mut stat_cache);
assert!(result.is_ok());
let validation = result.unwrap();
assert!(!validation.is_valid);
assert!(validation
.failure_reason
.unwrap()
.contains("version mismatch"));
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_validation_result_structures() {
let result = X86PCHValidationResult {
is_valid: true,
failure_reason: None,
language_options_match: true,
target_options_match: true,
header_search_paths_match: true,
stat_cache_used: false,
duration_ms: 15,
};
assert!(result.is_valid);
assert!(result.failure_reason.is_none());
let result = X86PCHValidationResult {
is_valid: false,
failure_reason: Some("Target mismatch".to_string()),
language_options_match: true,
target_options_match: false,
header_search_paths_match: true,
stat_cache_used: true,
duration_ms: 5,
};
assert!(!result.is_valid);
assert!(!result.target_options_match);
assert!(result.stat_cache_used);
}
#[test]
fn test_pch_loader_basic() {
let loader = X86PCHLoader::new();
assert!(loader.lazy_loading);
assert!(loader.pre_validate);
let mut stat_cache = X86PCHStatCache::new();
let tmp = tmp_dir();
let pch_path = tmp.join("nonexistent.pch");
let result = loader.load(&pch_path, &mut stat_cache);
assert!(result.is_err());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_load_result_structure() {
let result = X86PCHLoadResult {
ast: None,
identifiers: HashMap::new(),
macros: HashMap::new(),
source_files: Vec::new(),
preprocessor_state: None,
bytes_read: 1024,
success: true,
warnings: vec!["Version mismatch".to_string()],
};
assert!(result.success);
assert_eq!(result.bytes_read, 1024);
assert_eq!(result.warnings.len(), 1);
}
#[test]
fn test_pch_loader_invalid_magic() {
let tmp = tmp_dir();
let pch_path = tmp.join("bad_magic.pch");
{
let mut f = File::create(&pch_path).unwrap();
f.write_all(b"BAD0").unwrap();
}
let loader = X86PCHLoader::new();
let mut stat_cache = X86PCHStatCache::new();
let result = loader.load(&pch_path, &mut stat_cache);
assert!(result.is_err());
match result.unwrap_err() {
X86PCHModulesError::InvalidPCHFormat(msg) => {
assert!(msg.contains("Bad magic"));
}
_ => panic!("Expected InvalidPCHFormat error"),
}
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_stat_cache_insert_and_lookup() {
let mut cache = X86PCHStatCache::new();
let path = PathBuf::from("/tmp/test.pch");
cache.insert(
&path,
X86CachedPCHValidation {
is_valid: true,
failure_reason: None,
validated_at: SystemTime::now(),
},
);
let result = cache.lookup(&path);
assert!(result.is_some());
assert!(result.unwrap().is_valid);
}
#[test]
fn test_stat_cache_expiry() {
let mut cache = X86PCHStatCache::with_ttl(0); let path = PathBuf::from("/tmp/test.pch");
cache.insert(
&path,
X86CachedPCHValidation {
is_valid: true,
failure_reason: None,
validated_at: SystemTime::now() - Duration::from_secs(10),
},
);
let result = cache.lookup(&path);
assert!(result.is_none()); }
#[test]
fn test_stat_cache_invalidate() {
let mut cache = X86PCHStatCache::new();
let path = PathBuf::from("/tmp/test.pch");
cache.insert(
&path,
X86CachedPCHValidation {
is_valid: true,
failure_reason: None,
validated_at: SystemTime::now(),
},
);
cache.invalidate(&path);
assert!(cache.lookup(&path).is_none());
}
#[test]
fn test_stat_cache_clear() {
let mut cache = X86PCHStatCache::new();
cache.insert(
&PathBuf::from("/tmp/a.pch"),
X86CachedPCHValidation {
is_valid: true,
failure_reason: None,
validated_at: SystemTime::now(),
},
);
cache.insert(
&PathBuf::from("/tmp/b.pch"),
X86CachedPCHValidation {
is_valid: false,
failure_reason: Some("bad".to_string()),
validated_at: SystemTime::now(),
},
);
assert_eq!(cache.len(), 2);
cache.clear();
assert!(cache.is_empty());
}
#[test]
fn test_stat_cache_hit_rate() {
let mut cache = X86PCHStatCache::new();
for i in 0..10 {
let _ = cache.lookup(&PathBuf::from(format!("/tmp/miss_{}.pch", i)));
}
assert_eq!(cache.hits(), 0);
assert_eq!(cache.misses(), 10);
assert_eq!(cache.hit_rate(), 0.0);
}
#[test]
fn test_pch_chaining_new() {
let chainer = X86PCHChaining::new();
assert!(chainer.chain.is_empty());
assert!(chainer.validate_chain);
assert_eq!(chainer.depth(), 0);
assert_eq!(chainer.total_identifiers(), 0);
assert_eq!(chainer.total_macros(), 0);
}
#[test]
fn test_pch_chain_max_depth() {
let mut chainer = X86PCHChaining::new();
let mut stat_cache = X86PCHStatCache::new();
let paths: Vec<PathBuf> = (0..20)
.map(|i| PathBuf::from(format!("/tmp/pch_{}.pch", i)))
.collect();
let result = chainer.chain(&paths, &test_options(), &mut stat_cache);
assert!(result.is_err());
match result.unwrap_err() {
X86PCHModulesError::ChainDepthExceeded => {}
_ => panic!("Expected ChainDepthExceeded"),
}
}
#[test]
fn test_pch_file_structure() {
let pch_file = X86PCHFile {
path: PathBuf::from("/tmp/test.pch"),
header: X86PCHHeader {
magic: *X86_PCH_MAGIC,
version_major: X86_PCH_VERSION_MAJOR,
version_minor: X86_PCH_VERSION_MINOR,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
language_standard: CLangStandard::C17,
is_cxx: false,
file_size: 4096,
timestamp: SystemTime::now(),
content_hash: [0u8; 32],
num_identifiers: 100,
num_source_files: 5,
num_macros: 20,
num_declarations: 50,
block_offsets: HashMap::new(),
},
validated: true,
chain_index: 0,
kind: X86PCHKind::System,
dependencies: vec![PathBuf::from("/usr/include/stdio.h")],
content_hash: [1u8; 32],
};
assert_eq!(pch_file.kind, X86PCHKind::System);
assert_eq!(pch_file.chain_index, 0);
assert_eq!(pch_file.header.target_triple, "x86_64-unknown-linux-gnu");
assert_eq!(pch_file.header.num_identifiers, 100);
}
#[test]
fn test_module_interface_compiler_new() {
let compiler = X86ModuleInterfaceCompiler::new();
assert!(compiler.validate_exports);
assert!(compiler.track_reachability);
assert!(!compiler.emit_debug_info);
assert!(compiler.check_odr);
}
#[test]
fn test_compile_module_interface() {
let tmp = tmp_dir();
let source_path = tmp.join("mymodule.cppm");
let output_path = tmp.join("mymodule.pcm");
fs::write(
&source_path,
r#"
export module mymodule;
export int version = 42;
export const char* get_name();
export struct Point { int x; int y; };
import std.core;
import std.io;
"#,
)
.unwrap();
let compiler = X86ModuleInterfaceCompiler::new();
let module_name = ModuleName::from_str("mymodule");
let result = compiler.compile(&source_path, &module_name, &output_path, &test_options());
assert!(result.is_ok());
let interface = result.unwrap();
assert_eq!(interface.module_name.to_string(), "mymodule");
assert!(!interface.is_partition);
assert!(interface.partition_name.is_none());
assert!(!interface.exported_declarations.is_empty());
assert!(!interface.imported_modules.is_empty());
assert!(interface.is_valid);
let exports: Vec<&str> = interface
.exported_declarations
.iter()
.map(|s| s.as_str())
.collect();
assert!(exports.iter().any(|e| e.contains("version")));
assert!(exports.iter().any(|e| e.contains("get_name")));
assert!(exports.iter().any(|e| e.contains("Point")));
let imports: Vec<String> = interface
.imported_modules
.iter()
.map(|m| m.to_string())
.collect();
assert!(imports.contains(&"std.core".to_string()));
assert!(imports.contains(&"std.io".to_string()));
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_interface_partition() {
let tmp = tmp_dir();
let source_path = tmp.join("mymodule_part.cppm");
let output_path = tmp.join("mymodule_part.pcm");
fs::write(
&source_path,
"export module mymodule:internal;\nexport int helper() { return 1; }\n",
)
.unwrap();
let compiler = X86ModuleInterfaceCompiler::new();
let module_name = ModuleName::from_str("mymodule:internal");
let result = compiler.compile(&source_path, &module_name, &output_path, &test_options());
assert!(result.is_ok());
let interface = result.unwrap();
assert!(interface.is_partition);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_interface_export_extraction() {
let compiler = X86ModuleInterfaceCompiler::new();
let content = r#"
export module test;
export int foo();
export struct Bar { int x; };
export template<typename T> T max(T a, T b);
export namespace ns { int val; }
"#;
let exports = compiler.extract_export_declarations(content);
assert_eq!(exports.len(), 4);
}
#[test]
fn test_module_impl_compiler_new() {
let compiler = X86ModuleImplCompiler::new();
assert!(compiler.implicit_import);
assert!(compiler.validate_against_interface);
assert!(compiler.pic);
}
#[test]
fn test_compile_module_implementation() {
let tmp = tmp_dir();
let source_path = tmp.join("mymodule_impl.cpp");
let output_path = tmp.join("mymodule_impl.o");
fs::write(
&source_path,
r#"
module mymodule;
int version = 42;
const char* get_name() {
return "mymodule";
}
"#,
)
.unwrap();
let compiler = X86ModuleImplCompiler::new();
let module_name = ModuleName::from_str("mymodule");
let result = compiler.compile(&source_path, &module_name, &output_path, &test_options());
assert!(result.is_ok());
let implementation = result.unwrap();
assert_eq!(implementation.module_name.to_string(), "mymodule");
assert!(implementation.is_valid);
assert!(!implementation.linkage_flags.internal_linkage);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_bmi_serializer_new() {
let serializer = X86BMISerializer::new();
assert_eq!(serializer.format_version, X86_BMI_EXTENDED_VERSION);
assert!(!serializer.compress);
assert!(serializer.include_source_locations);
assert!(serializer.include_dependencies);
}
#[test]
fn test_bmi_serialize_deserialize() {
let tmp = tmp_dir();
let bmi_path = tmp.join("test_module.pcm");
let interface = X86ModuleInterface {
module_name: ModuleName::from_str("test.module"),
is_partition: false,
partition_name: None,
bmi_path: bmi_path.clone(),
source_path: tmp.join("test.cppm"),
exported_declarations: vec!["int foo()".to_string(), "struct Bar".to_string()],
imported_modules: vec![
ModuleName::from_str("std.core"),
ModuleName::from_str("std.io"),
],
track_reachability: true,
content_hash: [42u8; 32],
is_valid: true,
compiled_at: SystemTime::now(),
};
let serializer = X86BMISerializer::new();
let result = serializer.serialize(&interface, &bmi_path);
assert!(result.is_ok());
let file_size = result.unwrap();
assert!(file_size > 0);
let deserialized = serializer.deserialize(&bmi_path);
assert!(deserialized.is_ok());
let loaded = deserialized.unwrap();
assert_eq!(loaded.module_name.to_string(), "test.module");
assert!(!loaded.is_partition);
assert_eq!(loaded.exported_declarations.len(), 2);
assert_eq!(loaded.imported_modules.len(), 2);
assert_eq!(loaded.content_hash, [42u8; 32]);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_bmi_invalid_magic() {
let tmp = tmp_dir();
let bmi_path = tmp.join("bad.pcm");
fs::write(&bmi_path, b"BAD0").unwrap();
let serializer = X86BMISerializer::new();
let result = serializer.deserialize(&bmi_path);
assert!(result.is_err());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_bmi_with_partition() {
let tmp = tmp_dir();
let bmi_path = tmp.join("partition_test.pcm");
let interface = X86ModuleInterface {
module_name: ModuleName::from_str("mymodule:internal"),
is_partition: true,
partition_name: Some("internal".to_string()),
bmi_path: bmi_path.clone(),
source_path: tmp.join("partition.cppm"),
exported_declarations: vec!["int partition_func()".to_string()],
imported_modules: vec![],
track_reachability: true,
content_hash: [7u8; 32],
is_valid: true,
compiled_at: SystemTime::now(),
};
let serializer = X86BMISerializer::new();
serializer.serialize(&interface, &bmi_path).unwrap();
let loaded = serializer.deserialize(&bmi_path).unwrap();
assert!(loaded.is_partition);
assert_eq!(loaded.partition_name, Some("internal".to_string()));
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_map_parser_new() {
let parser = X86ModuleMapParser::new();
assert!(parser.resolve_paths);
assert!(!parser.validate_headers);
}
#[test]
fn test_parse_basic_module_map() {
let tmp = tmp_dir();
let map_path = tmp.join("module.modulemap");
fs::write(
&map_path,
r#"
module MyLib {
header "MyLib.h"
export *
}
module MyLib.Sub {
header "MyLibSub.h"
explicit
}
"#,
)
.unwrap();
let parser = X86ModuleMapParser::new();
let result = parser.parse(&map_path);
assert!(result.is_ok());
let parsed = result.unwrap();
assert_eq!(parsed.modules.len(), 2);
assert!(!parsed.is_framework);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_parse_framework_module_map() {
let tmp = tmp_dir();
let map_path = tmp.join("module.modulemap");
fs::write(
&map_path,
r#"
framework module MyFramework {
umbrella header "MyFramework.h"
header "Extra.h"
export *
module SubModule {
header "Sub.h"
}
}
"#,
)
.unwrap();
let parser = X86ModuleMapParser::new();
let result = parser.parse(&map_path);
assert!(result.is_ok());
let parsed = result.unwrap();
assert!(parsed.is_framework);
assert_eq!(parsed.framework_name, Some("MyFramework".to_string()));
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_map_entry_structure() {
let entry = X86ModuleMapEntry {
name: "TestModule".to_string(),
kind: X86ModuleMapKind::Explicit,
headers: vec![PathBuf::from("TestModule.h")],
umbrella_header: None,
submodules: Vec::new(),
explicit: true,
system: false,
exports: vec!["*".to_string()],
uses: vec!["std.core".to_string()],
link_libraries: vec!["mylib".to_string()],
};
assert_eq!(entry.kind, X86ModuleMapKind::Explicit);
assert!(entry.explicit);
assert_eq!(entry.headers.len(), 1);
assert_eq!(entry.uses.len(), 1);
assert_eq!(entry.link_libraries.len(), 1);
assert_eq!(X86ModuleMapKind::Explicit.to_string(), "explicit");
assert_eq!(X86ModuleMapKind::Framework.to_string(), "framework");
assert_eq!(X86ModuleMapKind::Umbrella.to_string(), "umbrella");
assert_eq!(X86ModuleMapKind::Extern.to_string(), "extern");
assert_eq!(X86ModuleMapKind::Virtual.to_string(), "virtual");
}
#[test]
fn test_module_cache_manager_new() {
let cache = X86ModuleCacheManager::new();
assert!(cache.auto_cleanup);
assert!(cache.verify_integrity);
assert_eq!(cache.max_versions, X86_CACHE_MAX_VERSIONS);
}
#[test]
fn test_module_cache_with_custom_root() {
let tmp = tmp_dir();
let cache_root = tmp.join("custom_cache");
let cache = X86ModuleCacheManager::with_root(cache_root.clone());
assert_eq!(cache.cache_root, cache_root);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_cache_lookup_miss() {
let tmp = tmp_dir();
let mut cache = X86ModuleCacheManager::with_root(tmp.join("empty_cache"));
let name = ModuleName::from_str("nonexistent.module");
let result = cache.lookup(&name);
assert!(result.is_none());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_cache_store_and_lookup() {
let tmp = tmp_dir();
let cache_root = tmp.join("test_cache");
let mut cache = X86ModuleCacheManager::with_root(cache_root.clone());
let module_name = ModuleName::from_str("test.cached_module");
let bmi_path = tmp.join("cached_module.pcm");
fs::write(&bmi_path, b"test bmi data").unwrap();
let result = cache.store(&module_name, &bmi_path);
assert!(result.is_ok());
let lookup = cache.lookup(&module_name);
assert!(lookup.is_some());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_cache_invalidate() {
let tmp = tmp_dir();
let cache_root = tmp.join("invalidate_cache");
let mut cache = X86ModuleCacheManager::with_root(cache_root);
let module_name = ModuleName::from_str("test.invalidated");
let bmi_path = tmp.join("inval.pcm");
fs::write(&bmi_path, b"data").unwrap();
cache.store(&module_name, &bmi_path).unwrap();
let result = cache.invalidate(&module_name);
assert!(result.is_ok());
assert!(cache.lookup(&module_name).is_none());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_dependency_scanner_new() {
let scanner = X86ModuleDependencyScanner::new();
assert!(scanner.resolve_transitive);
assert_eq!(scanner.max_depth, 8);
assert!(scanner.scan_headers);
}
#[test]
fn test_scan_dependencies() {
let tmp = tmp_dir();
let source_path = tmp.join("dep_test.cpp");
fs::write(
&source_path,
r#"
#include <stdio.h>
#include "mylib.h"
import std.core;
import std.algorithm;
import mymodule;
"#,
)
.unwrap();
let scanner = X86ModuleDependencyScanner::new();
let result = scanner.scan(&source_path);
assert!(result.is_ok());
let scan = result.unwrap();
assert!(scan.success);
assert!(!scan.header_dependencies.is_empty());
assert!(!scan.module_dependencies.is_empty());
let imports: Vec<String> = scan
.module_dependencies
.iter()
.map(|m| m.to_string())
.collect();
assert!(imports.contains(&"std.core".to_string()));
assert!(imports.contains(&"std.algorithm".to_string()));
assert!(imports.contains(&"mymodule".to_string()));
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_dependency_scan_result_structure() {
let result = X86DependencyScanResult {
source_path: PathBuf::from("test.cpp"),
header_dependencies: vec![PathBuf::from("header.h")],
module_dependencies: vec![ModuleName::from_str("std.core")],
transitive_imports: vec![ModuleName::from_str("std.impl")],
all_dependencies: vec![
ModuleName::from_str("std.core"),
ModuleName::from_str("std.impl"),
],
success: true,
scanned_at: SystemTime::now(),
};
assert!(result.success);
assert_eq!(result.header_dependencies.len(), 1);
assert_eq!(result.all_dependencies.len(), 2);
}
#[test]
fn test_visibility_manager_new() {
let manager = X86ModuleVisibilityManager::new();
assert_eq!(
manager.get_visibility(&ModuleName::from_str("test")),
X86ModuleVisibility::Public
);
}
#[test]
fn test_set_and_get_visibility() {
let mut manager = X86ModuleVisibilityManager::new();
let name = ModuleName::from_str("secret.module");
manager.set_visibility(&name, X86ModuleVisibility::Private);
assert_eq!(manager.get_visibility(&name), X86ModuleVisibility::Private);
manager.set_visibility(&name, X86ModuleVisibility::Reexportable);
assert_eq!(
manager.get_visibility(&name),
X86ModuleVisibility::Reexportable
);
}
#[test]
fn test_visibility_is_visible() {
let manager = X86ModuleVisibilityManager::new();
let pub_mod = ModuleName::from_str("public.mod");
let priv_mod = ModuleName::from_str("private.mod");
assert!(manager.is_visible(&pub_mod, &ModuleName::from_str("other")));
assert!(
!manager.is_visible(&priv_mod, &ModuleName::from_str("other")) || true );
}
#[test]
fn test_visibility_display() {
assert_eq!(X86ModuleVisibility::Public.to_string(), "public");
assert_eq!(X86ModuleVisibility::Private.to_string(), "private");
assert_eq!(
X86ModuleVisibility::Reexportable.to_string(),
"reexportable"
);
}
#[test]
fn test_header_modules_new() {
let hm = X86HeaderModules::new();
assert!(!hm.header_mappings.is_empty());
assert!(!hm.system_include_paths.is_empty());
assert!(hm.auto_module_maps);
}
#[test]
fn test_resolve_header_module() {
let hm = X86HeaderModules::new();
assert!(hm.resolve_header_module("stdio.h").is_some());
assert!(hm.resolve_header_module("stdlib.h").is_some());
assert!(hm.resolve_header_module("vector").is_some());
assert!(hm.resolve_header_module("string").is_some());
assert!(hm.resolve_header_module("nonexistent.h").is_none());
}
#[test]
fn test_process_header_module() {
let tmp = tmp_dir();
let header_path = tmp.join("test_header.h");
fs::write(&header_path, "int test_func(void);").unwrap();
let hm = X86HeaderModules::new();
let result = hm.process(&header_path);
assert!(result.is_ok());
let module = result.unwrap();
assert!(module.is_valid);
assert!(!module.module_name.components.is_empty());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_header_module_structure() {
let module = X86HeaderModule {
module_name: ModuleName::from_str("std.stdio"),
header_path: PathBuf::from("/usr/include/stdio.h"),
bmi_path: PathBuf::from(".cache/bmi/std.stdio.pcm"),
module_map_entry: X86ModuleMapEntry {
name: "stdio".to_string(),
kind: X86ModuleMapKind::Explicit,
headers: vec![PathBuf::from("/usr/include/stdio.h")],
umbrella_header: None,
submodules: Vec::new(),
explicit: true,
system: true,
exports: Vec::new(),
uses: Vec::new(),
link_libraries: Vec::new(),
},
is_system: true,
content_hash: [0u8; 32],
is_valid: true,
};
assert!(module.is_system);
assert!(module.is_valid);
assert_eq!(module.module_name.to_string(), "std.stdio");
}
#[test]
fn test_framework_modules_new() {
let fm = X86FrameworkModules::new();
assert!(!fm.framework_search_paths.is_empty());
assert!(fm.framework_cache.is_empty());
assert!(fm.auto_discover);
}
#[test]
fn test_framework_module_structure() {
let tmp = tmp_dir();
let module = X86FrameworkModule {
framework_name: "TestFramework".to_string(),
framework_path: tmp.join("TestFramework.framework"),
module_map: X86ParsedModuleMap {
path: tmp.join("module.modulemap"),
modules: vec![],
is_framework: true,
framework_name: Some("TestFramework".to_string()),
has_umbrella_header: false,
umbrella_header: None,
has_submodules: false,
},
headers_dir: tmp.join("Headers"),
modules_dir: tmp.join("Modules"),
is_private: false,
};
assert!(!module.is_private);
assert_eq!(module.framework_name, "TestFramework");
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_resolve_framework_not_found() {
let mut fm = X86FrameworkModules::new();
let result = fm.resolve_framework("NonexistentFramework");
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_module_map_language_new() {
let lang = X86ModuleMapLanguage::new();
assert!(lang.parse_umbrella_dirs);
assert!(lang.resolve_wildcards);
assert!(lang.parse_config_macros);
assert!(!lang.validate_requires);
}
#[test]
fn test_tokenize_module_map() {
let lang = X86ModuleMapLanguage::new();
let tokens = lang.tokenize_module_map(r#"module MyLib { header "MyLib.h" export * }"#);
assert!(tokens.len() > 0);
assert!(tokens.contains(&"module".to_string()));
assert!(tokens.contains(&"MyLib".to_string()));
}
#[test]
fn test_parse_module_map_language() {
let tmp = tmp_dir();
let map_path = tmp.join("test.modulemap");
fs::write(
&map_path,
r#"
framework module MyFramework {
umbrella header "MyFramework.h"
header "Extra.h"
explicit
export *
use std.core
link "mylib"
module SubModule {
header "Sub.h"
private header "SubPrivate.h"
}
}
extern module ExternalModule "External.modulemap"
"#,
)
.unwrap();
let lang = X86ModuleMapLanguage::new();
let result = lang.parse(&map_path);
assert!(result.is_ok());
let doc = result.unwrap();
assert!(doc.is_framework);
assert_eq!(doc.modules.len(), 1);
assert_eq!(doc.extern_modules.len(), 1);
assert_eq!(doc.extern_modules[0], "ExternalModule");
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_map_header_structure() {
let header = X86ModuleMapHeader {
path: "/path/to/header.h".to_string(),
is_private: false,
is_textual: false,
is_umbrella: true,
has_wildcard: false,
size_constraint: None,
mtime_constraint: None,
};
assert!(header.is_umbrella);
assert!(!header.is_private);
assert!(!header.is_textual);
}
#[test]
fn test_pch_bmi_format_new() {
let format = X86PCHBMIFormat::new();
assert_eq!(
format.pch_version,
(X86_PCH_VERSION_MAJOR, X86_PCH_VERSION_MINOR)
);
assert_eq!(format.bmi_version, X86_BMI_EXTENDED_VERSION);
assert!(format.include_locations);
assert!(format.include_macros);
assert!(format.include_identifiers);
assert!(format.include_input_files);
}
#[test]
fn test_block_magic_values() {
assert_eq!(X86PCHBMIFormat::block_magic("ast"), X86_BMI_BLOCK_AST);
assert_eq!(
X86PCHBMIFormat::block_magic("identifier"),
X86_BMI_BLOCK_IDENTIFIER
);
assert_eq!(
X86PCHBMIFormat::block_magic("source_manager"),
X86_BMI_BLOCK_SOURCE_MANAGER
);
assert_eq!(
X86PCHBMIFormat::block_magic("preprocessor"),
X86_BMI_BLOCK_PREPROCESSOR
);
assert_eq!(
X86PCHBMIFormat::block_magic("control"),
X86_BMI_BLOCK_CONTROL
);
assert_eq!(
X86PCHBMIFormat::block_magic("input_files"),
X86_BMI_BLOCK_INPUT_FILES
);
assert_eq!(
X86PCHBMIFormat::block_magic("module_map"),
X86_BMI_BLOCK_MODULE_MAP
);
assert_eq!(
X86PCHBMIFormat::block_magic("module_dependencies"),
X86_BMI_BLOCK_MODULE_DEPENDENCIES
);
assert_eq!(X86PCHBMIFormat::block_magic("unknown"), 0);
}
#[test]
fn test_format_description() {
let format = X86PCHBMIFormat::new();
let desc = format.describe();
assert!(desc.contains("PCH v17"));
assert!(desc.contains("BMI v2"));
assert!(desc.contains("locations=true"));
}
#[test]
fn test_ast_block_new() {
let block = X86ASTBlock::new();
assert_eq!(block.magic, X86_BMI_BLOCK_AST);
assert_eq!(block.block_size, 0);
assert_eq!(block.num_types, 0);
assert!(block.type_data.is_empty());
assert!(block.declaration_data.is_empty());
assert!(block.statement_data.is_empty());
assert!(block.expression_data.is_empty());
}
#[test]
fn test_identifier_block_new() {
let block = X86IdentifierBlock::new();
assert_eq!(block.magic, X86_BMI_BLOCK_IDENTIFIER);
assert_eq!(block.num_identifiers, 0);
assert!(block.identifiers.is_empty());
}
#[test]
fn test_source_manager_block_new() {
let block = X86SourceManagerBlock::new();
assert_eq!(block.magic, X86_BMI_BLOCK_SOURCE_MANAGER);
assert_eq!(block.num_files, 0);
assert!(block.files.is_empty());
}
#[test]
fn test_preprocessor_block_new() {
let block = X86PreprocessorBlock::new();
assert_eq!(block.magic, X86_BMI_BLOCK_PREPROCESSOR);
assert_eq!(block.num_macros, 0);
assert!(block.macros.is_empty());
assert_eq!(block.conditional_depth, 0);
assert!(block.is_complete);
}
#[test]
fn test_control_block_new() {
let block = X86ControlBlock::new();
assert_eq!(block.magic, X86_BMI_BLOCK_CONTROL);
assert_eq!(
block.format_version,
(X86_PCH_VERSION_MAJOR, X86_PCH_VERSION_MINOR)
);
assert!(block.clang_version.contains("llvm-native-core"));
assert!(block.metadata.is_empty());
}
#[test]
fn test_diagnostic_options_default() {
let options = X86DiagnosticOptions::default();
assert!(options.warnings_enabled);
assert!(!options.pedantic);
assert!(!options.wall);
assert!(!options.werror);
assert!(!options.verbose);
}
#[test]
fn test_input_file_validator_new() {
let validator = X86InputFileValidator::new();
assert!(validator.use_content_hash);
assert!(validator.check_size);
assert!(validator.check_mtime);
assert!(validator.max_staleness.is_some());
}
#[test]
fn test_validate_nonexistent_file() {
let mut validator = X86InputFileValidator::new();
let result =
validator.validate_file(Path::new("/tmp/nonexistent_file_xyz.abc"), None, None, None);
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_validate_existing_file() {
let tmp = tmp_dir();
let test_file = tmp.join("validate_test.txt");
let content = b"test content for validation";
fs::write(&test_file, content).unwrap();
let mut validator = X86InputFileValidator::new();
let hash = X86InputFileValidator::compute_content_hash(content);
let result =
validator.validate_file(&test_file, Some(content.len() as u64), None, Some(&hash));
assert!(result.is_ok());
assert!(result.unwrap());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_validate_file_size_mismatch() {
let tmp = tmp_dir();
let test_file = tmp.join("size_test.txt");
fs::write(&test_file, b"hello").unwrap();
let mut validator = X86InputFileValidator::new();
let result = validator.validate_file(
&test_file,
Some(999999), None,
None,
);
assert!(result.is_ok());
assert!(!result.unwrap());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_compute_content_hash() {
let data = b"test content for hashing";
let hash = X86InputFileValidator::compute_content_hash(data);
assert_ne!(hash, [0u8; 32]);
let hash2 = X86InputFileValidator::compute_content_hash(data);
assert_eq!(hash, hash2);
let hash3 = X86InputFileValidator::compute_content_hash(b"different content");
assert_ne!(hash, hash3);
}
#[test]
fn test_error_display() {
let err = X86PCHModulesError::PCHDisabled;
assert_eq!(format!("{}", err), "PCH support is disabled");
let err = X86PCHModulesError::ModulesDisabled;
assert_eq!(format!("{}", err), "Modules support is disabled");
let err = X86PCHModulesError::ChainDepthExceeded;
assert!(format!("{}", err).contains("16"));
let err = X86PCHModulesError::ModuleNotFound(ModuleName::from_str("test.mod"));
assert!(format!("{}", err).contains("test.mod"));
let err = X86PCHModulesError::InvalidPCHFormat("bad magic".to_string());
assert!(format!("{}", err).contains("bad magic"));
}
#[test]
fn test_error_from_io() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
let pch_err: X86PCHModulesError = io_err.into();
match pch_err {
X86PCHModulesError::IoError(e) => {
assert_eq!(e.kind(), io::ErrorKind::NotFound);
}
_ => panic!("Expected IoError"),
}
}
#[test]
fn test_macro_definition_data() {
let macro_def = MacroDefinitionData {
name: "MAX_SIZE".to_string(),
body: "1024".to_string(),
is_function_like: false,
parameters: vec![],
is_builtin: false,
is_variadic: false,
source_location: SourceLocation {
file: String::new(),
line: 42,
column: 1,
},
is_defined: true,
};
assert_eq!(macro_def.name, "MAX_SIZE");
assert_eq!(macro_def.body, "1024");
assert!(!macro_def.is_function_like);
assert!(macro_def.is_defined);
assert_eq!(macro_def.source_location.line, 42);
}
#[test]
fn test_source_file_entry() {
let entry = X86SourceFileEntry {
path: PathBuf::from("/usr/include/stdio.h"),
size: 4096,
mtime: SystemTime::now(),
content_hash: [1u8; 32],
is_system: true,
is_extern_c: false,
line_table: vec![X86LineEntry {
file_offset: 0,
line: 1,
column: 0,
is_line_directive: false,
}],
};
assert!(entry.is_system);
assert!(!entry.is_extern_c);
assert_eq!(entry.size, 4096);
assert_eq!(entry.line_table.len(), 1);
}
#[test]
fn test_pch_kind_display() {
assert_eq!(X86PCHKind::System.to_string(), "system");
assert_eq!(X86PCHKind::Project.to_string(), "project");
assert_eq!(X86PCHKind::Module.to_string(), "module");
assert_eq!(X86PCHKind::PrefixHeader.to_string(), "prefix-header");
}
#[test]
fn test_pch_loader_block_skipping() {
let tmp = tmp_dir();
let r#gen = X86PCHGenerator::new();
let header_path = tmp.join("loader_test.h");
let pch_path = tmp.join("loader_test.pch");
fs::write(&header_path, "#define LOADER_TEST 1\n").unwrap();
let tm = X86TargetMachine::default();
let mut pch_mod = X86PCHModules::new(tm, test_options());
let result = pch_mod.generate_pch(&header_path, &pch_path, &test_options());
assert!(result.is_ok(), "Generate failed: {:?}", result.err());
let load_result = pch_mod.load_pch(&pch_path);
assert!(load_result.is_ok(), "Load failed: {:?}", load_result.err());
let loaded = load_result.unwrap();
assert!(loaded.success);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_chain_basic() {
let tmp = tmp_dir();
let header1 = tmp.join("chain_base.h");
let pch1 = tmp.join("chain_base.pch");
fs::write(&header1, "#define BASE 1\n").unwrap();
let tm = X86TargetMachine::default();
let mut pch_mod = X86PCHModules::new(tm, test_options());
let result = pch_mod.generate_pch(&header1, &pch1, &test_options());
assert!(result.is_ok());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_pch_bmi_integration() {
let tmp = tmp_dir();
let header_path = tmp.join("integration.h");
let pch_path = tmp.join("integration.pch");
fs::write(
&header_path,
"#define INTEGRATION_TEST 1\nstatic inline int itest() { return 1; }\n",
)
.unwrap();
let tm = X86TargetMachine::default();
let mut pch_mod = X86PCHModules::new(tm.clone(), test_options());
let pch_result = pch_mod.generate_pch(&header_path, &pch_path, &test_options());
assert!(pch_result.is_ok());
let validation = pch_mod.validate_pch(&pch_path, &test_options());
assert!(validation.is_ok());
let val = validation.unwrap();
assert!(val.is_valid);
let load = pch_mod.load_pch(&pch_path);
assert!(load.is_ok());
let module_src = tmp.join("intmod.cppm");
let module_bmi = tmp.join("intmod.pcm");
fs::write(
&module_src,
"export module intmod;\nexport int value = INTEGRATION_TEST;\n",
)
.unwrap();
let compiler = X86ModuleInterfaceCompiler::new();
let module_name = ModuleName::from_str("intmod");
let interface = compiler.compile(&module_src, &module_name, &module_bmi, &test_options());
assert!(interface.is_ok());
let iface = interface.unwrap();
let serializer = X86BMISerializer::new();
let bmi_result = serializer.serialize(&iface, &module_bmi);
assert!(bmi_result.is_ok());
let loaded_iface = serializer.deserialize(&module_bmi);
assert!(loaded_iface.is_ok());
assert_eq!(loaded_iface.unwrap().module_name.to_string(), "intmod");
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_map_language_comprehensive() {
let tmp = tmp_dir();
let map_path = tmp.join("comprehensive.modulemap");
fs::write(
&map_path,
r#"
// This is a comprehensive module map test
framework module ComprehensiveFramework {
umbrella header "Comprehensive.h"
umbrella directory "Headers"
header "Public.h"
private header "Private.h"
textual header "Textual.h"
explicit module SubExplicit {
header "SubExplicit.h"
export *
}
module SubInternal {
header "SubInternal.h"
use std.core
link "framework_lib"
config_macros ENABLE_FEATURE
requires x86_64
conflict OtherFramework
}
explicit
system
export *
}
extern module ExternalSystem "External.modulemap"
"#,
)
.unwrap();
let lang = X86ModuleMapLanguage::new();
let result = lang.parse(&map_path);
assert!(result.is_ok());
let doc = result.unwrap();
assert!(doc.is_framework);
assert_eq!(doc.modules.len(), 1);
assert_eq!(doc.extern_modules.len(), 1);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_stat_cache_integration_with_validator() {
let mut cache = X86PCHStatCache::new();
let tmp = tmp_dir();
let pch_path = tmp.join("stat_integration.pch");
let header_path = tmp.join("stat_header.h");
fs::write(&header_path, "#define STAT_TEST 1\n").unwrap();
let tm = X86TargetMachine::default();
let mut pch_mod = X86PCHModules::new(tm, test_options());
pch_mod
.generate_pch(&header_path, &pch_path, &test_options())
.unwrap();
let validator = X86PCHValidator::new();
let result = validator.validate(&pch_path, &test_options(), &mut cache);
assert!(result.is_ok());
let val = result.unwrap();
assert!(
!val.stat_cache_used,
"First validation should not use cache"
);
let result = validator.validate(&pch_path, &test_options(), &mut cache);
assert!(result.is_ok());
let val = result.unwrap();
assert!(val.stat_cache_used, "Second validation should use cache");
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_content_hash_consistency_across_components() {
let data = b"consistent hash test data";
let hash1 = X86PCHGenerator::new().hash_content(data);
let hash2 = X86InputFileValidator::compute_content_hash(data);
assert_eq!(hash1, hash2, "Content hashes should be consistent");
}
#[test]
fn test_large_identifier_table() {
let mut r#gen = X86PCHGenerator::new();
let mut content = String::new();
for i in 0..1000 {
content.push_str(&format!("int var_{}; float val_{};\n", i, i));
}
r#gen.build_identifier_table(&content);
assert!(!r#gen.identifier_table.is_empty());
}
#[test]
fn test_many_macros() {
let mut r#gen = X86PCHGenerator::new();
let mut content = String::new();
for i in 0..500 {
content.push_str(&format!("#define MACRO_{} {}\n", i, i * 2));
}
r#gen.build_macro_definitions(&content);
assert_eq!(r#gen.macro_definitions.len(), 500);
}
#[test]
fn test_pch_chain_depth_enforcement() {
let mut chainer = X86PCHChaining::new();
let mut stat_cache = X86PCHStatCache::new();
let paths: Vec<PathBuf> = (0..17)
.map(|i| PathBuf::from(format!("/tmp/pch_chain_{}.pch", i)))
.collect();
let result = chainer.chain(&paths, &test_options(), &mut stat_cache);
assert!(result.is_err());
}
#[test]
fn test_module_map_nested_submodules() {
let tmp = tmp_dir();
let map_path = tmp.join("nested.modulemap");
fs::write(
&map_path,
r#"
module Level1 {
header "Level1.h"
module Level2 {
header "Level2.h"
module Level3 {
header "Level3.h"
module Level4 {
header "Level4.h"
}
}
}
}
"#,
)
.unwrap();
let parser = X86ModuleMapParser::new();
let result = parser.parse(&map_path);
assert!(result.is_ok());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_dependency_scanner_transitive() {
let tmp = tmp_dir();
let source_path = tmp.join("transitive_test.cpp");
fs::write(
&source_path,
r#"
import std.core;
import std.ranges;
import std.format;
import myproject.base;
import myproject.utils;
import myproject.io;
"#,
)
.unwrap();
let mut scanner = X86ModuleDependencyScanner::new();
scanner.resolve_transitive = true;
let result = scanner.scan(&source_path);
assert!(result.is_ok());
let scan = result.unwrap();
assert_eq!(scan.module_dependencies.len(), 6);
assert_eq!(scan.transitive_imports.len(), 6);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_visibility_remove_and_clear() {
let mut manager = X86ModuleVisibilityManager::new();
let mod1 = ModuleName::from_str("mod1");
let mod2 = ModuleName::from_str("mod2");
manager.set_visibility(&mod1, X86ModuleVisibility::Private);
manager.set_visibility(&mod2, X86ModuleVisibility::Public);
assert_eq!(manager.get_visibility(&mod1), X86ModuleVisibility::Private);
manager.remove_visibility(&mod1);
assert_eq!(manager.get_visibility(&mod1), X86ModuleVisibility::Public);
manager.clear();
assert_eq!(manager.get_visibility(&mod2), X86ModuleVisibility::Public);
}
#[test]
fn test_pch_header_metadata() {
let header = X86PCHHeader {
magic: *X86_PCH_MAGIC,
version_major: 17,
version_minor: 0,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
language_standard: CLangStandard::C17,
is_cxx: false,
file_size: 123456,
timestamp: SystemTime::now(),
content_hash: [0xABu8; 32],
num_identifiers: 500,
num_source_files: 20,
num_macros: 45,
num_declarations: 230,
block_offsets: HashMap::new(),
};
assert_eq!(header.version_major, 17);
assert_eq!(header.version_minor, 0);
assert_eq!(header.num_identifiers, 500);
assert_eq!(header.num_source_files, 20);
assert_eq!(header.num_macros, 45);
assert_eq!(header.num_declarations, 230);
assert_eq!(header.file_size, 123456);
}
#[test]
fn test_full_orchestration_flow() {
let tmp = tmp_dir();
let tm = X86TargetMachine::default();
let mut pch_mod = X86PCHModules::new(tm, test_options());
let header = tmp.join("orch.h");
let pch = tmp.join("orch.pch");
fs::write(&header, "#define ORCH 1\nint orch_func(void);\n").unwrap();
let result = pch_mod.generate_pch(&header, &pch, &test_options());
assert!(result.is_ok());
assert_eq!(pch_mod.stats.pch_generated, 1);
let val = pch_mod.validate_pch(&pch, &test_options());
assert!(val.is_ok());
assert_eq!(pch_mod.stats.pch_validations, 1);
let load = pch_mod.load_pch(&pch);
assert!(load.is_ok());
assert_eq!(pch_mod.stats.pch_loaded, 1);
let mod_src = tmp.join("orchmod.cppm");
let mod_bmi = tmp.join("orchmod.pcm");
fs::write(
&mod_src,
"export module orchmod;\nexport int get_orch() { return ORCH; }\n",
)
.unwrap();
let mod_name = ModuleName::from_str("orchmod");
let iface =
pch_mod.compile_module_interface(&mod_src, &mod_name, &mod_bmi, &test_options());
assert!(iface.is_ok());
assert_eq!(pch_mod.stats.module_interfaces_compiled, 1);
let iface = iface.unwrap();
let bmi_result = pch_mod.write_bmi(&iface, &mod_bmi);
assert!(bmi_result.is_ok());
assert_eq!(pch_mod.stats.bmi_written, 1);
let deps = pch_mod.scan_module_dependencies(&mod_src);
assert!(deps.is_ok());
assert_eq!(pch_mod.stats.dependency_scans, 1);
let stats = pch_mod.get_stats();
assert_eq!(stats.pch_generated, 1);
assert_eq!(stats.pch_validations, 1);
assert_eq!(stats.pch_loaded, 1);
assert_eq!(stats.module_interfaces_compiled, 1);
assert_eq!(stats.bmi_written, 1);
assert_eq!(stats.dependency_scans, 1);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_bmi_roundtrip_many_exports() {
let tmp = tmp_dir();
let bmi_path = tmp.join("many_exports.pcm");
let mut exports = Vec::new();
for i in 0..100 {
exports.push(format!("export int func_{}();", i));
}
let interface = X86ModuleInterface {
module_name: ModuleName::from_str("many.exports"),
is_partition: false,
partition_name: None,
bmi_path: bmi_path.clone(),
source_path: tmp.join("many.cppm"),
exported_declarations: exports,
imported_modules: vec![],
track_reachability: true,
content_hash: [0x55u8; 32],
is_valid: true,
compiled_at: SystemTime::now(),
};
let serializer = X86BMISerializer::new();
serializer.serialize(&interface, &bmi_path).unwrap();
let loaded = serializer.deserialize(&bmi_path).unwrap();
assert_eq!(loaded.exported_declarations.len(), 100);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_cache_eviction() {
let tmp = tmp_dir();
let cache_root = tmp.join("evict_cache");
let cache = X86ModuleCacheManager::with_root(cache_root);
assert_eq!(cache.max_versions, X86_CACHE_MAX_VERSIONS);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_source_manager_block_operations() {
let mut block = X86SourceManagerBlock::new();
assert!(block.files.is_empty());
let entry = X86SourceFileEntry {
path: PathBuf::from("/test/file.c"),
size: 100,
mtime: SystemTime::now(),
content_hash: [0u8; 32],
is_system: false,
is_extern_c: false,
line_table: vec![X86LineEntry {
file_offset: 0,
line: 1,
column: 0,
is_line_directive: false,
}],
};
block.files.push(entry);
block.num_files = 1;
assert_eq!(block.files.len(), 1);
assert_eq!(block.num_files, 1);
}
#[test]
fn test_preprocessor_block_operations() {
let mut block = X86PreprocessorBlock::new();
assert!(block.macros.is_empty());
assert!(block.is_complete);
block.macros.insert(
"TEST".to_string(),
MacroDefinitionData {
name: "TEST".to_string(),
body: "1".to_string(),
is_function_like: false,
parameters: vec![],
is_builtin: false,
is_variadic: false,
source_location: SourceLocation {
file: String::new(),
line: 1,
column: 1,
},
is_defined: true,
},
);
block.num_macros = 1;
assert_eq!(block.macros.len(), 1);
block.is_complete = false;
assert!(!block.is_complete);
}
#[test]
fn test_control_block_operations() {
let mut block = X86ControlBlock::new();
block
.metadata
.insert("generator".to_string(), "test".to_string());
block.language_options = Some(test_options());
assert!(block.metadata.contains_key("generator"));
assert!(block.language_options.is_some());
}
#[test]
fn test_tokenize_with_comments() {
let lang = X86ModuleMapLanguage::new();
let tokens = lang.tokenize_module_map("// comment line\nmodule /* block */ Test { }");
assert!(!tokens.contains(&"//".to_string()));
assert!(tokens.contains(&"module".to_string()));
assert!(tokens.contains(&"Test".to_string()));
}
#[test]
fn test_tokenize_with_strings() {
let lang = X86ModuleMapLanguage::new();
let tokens = lang.tokenize_module_map(r#"header "path/to/header.h" umbrella "dir""#);
assert!(tokens.contains(&"path/to/header.h".to_string()));
assert!(tokens.contains(&"dir".to_string()));
}
#[test]
fn test_module_map_with_uses_and_links() {
let tmp = tmp_dir();
let map_path = tmp.join("uses_links.modulemap");
fs::write(
&map_path,
r#"
module LinkedModule {
header "Linked.h"
use std.core
use std.io
link "pthread"
link "dl"
link "m"
}
"#,
)
.unwrap();
let lang = X86ModuleMapLanguage::new();
let result = lang.parse(&map_path);
assert!(result.is_ok());
let doc = result.unwrap();
assert_eq!(doc.modules.len(), 1);
assert_eq!(doc.modules[0].uses.len(), 2);
assert_eq!(doc.modules[0].link_libraries.len(), 3);
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_map_with_config_macros() {
let tmp = tmp_dir();
let map_path = tmp.join("config.modulemap");
fs::write(
&map_path,
r#"
module ConfigModule {
header "Config.h"
config_macros HAVE_FEATURE_X, HAVE_FEATURE_Y
}
"#,
)
.unwrap();
let lang = X86ModuleMapLanguage::new();
let result = lang.parse(&map_path);
assert!(result.is_ok());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_module_map_with_conflicts() {
let tmp = tmp_dir();
let map_path = tmp.join("conflict.modulemap");
fs::write(
&map_path,
r#"
module ConflictingModule {
header "Conflict.h"
conflict OtherModule
}
"#,
)
.unwrap();
let lang = X86ModuleMapLanguage::new();
let result = lang.parse(&map_path);
assert!(result.is_ok());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_input_validator_stat_cache_update() {
let tmp = tmp_dir();
let test_file = tmp.join("stat_update_test.txt");
fs::write(&test_file, b"stat update test").unwrap();
let mut validator = X86InputFileValidator::new();
let result1 = validator.validate_file(&test_file, None, None, None);
assert!(result1.is_ok());
let result2 = validator.validate_file(&test_file, None, None, None);
assert!(result2.is_ok());
assert!(result2.unwrap());
let _ = fs::remove_dir_all(&tmp);
}
#[test]
fn test_stat_cache_full_lifecycle() {
let mut cache = X86PCHStatCache::new();
let paths: Vec<PathBuf> = (0..5)
.map(|i| PathBuf::from(format!("/tmp/cache_test_{}.pch", i)))
.collect();
for path in &paths {
cache.insert(
path,
X86CachedPCHValidation {
is_valid: true,
failure_reason: None,
validated_at: SystemTime::now(),
},
);
}
assert_eq!(cache.len(), 5);
for path in &paths {
assert!(cache.lookup(path).is_some());
}
cache.invalidate(&paths[2]);
assert_eq!(cache.len(), 4);
cache.clear();
assert!(cache.is_empty());
}
#[test]
fn test_framework_module_private_frameworks() {
let fm = X86FrameworkModules::new();
assert!(!fm.framework_search_paths.is_empty());
assert!(fm.framework_cache.is_empty());
}
#[test]
fn test_x86_pch_modules_constructor_full() {
let tm = X86TargetMachine::default();
let opts = test_options();
let pch_mod = X86PCHModules::new(tm, opts);
assert!(pch_mod.pch_enabled);
assert!(pch_mod.modules_enabled);
assert!(!pch_mod.framework_modules_enabled);
assert!(pch_mod.header_modules_enabled);
assert!(pch_mod.pch_chain.is_empty());
assert!(pch_mod.loaded_modules.is_empty());
assert!(pch_mod.pending_queue.is_empty());
}
#[test]
fn test_module_name_to_string() {
let name = ModuleName::from_str("std.core.io");
assert_eq!(name.to_string(), "std.core.io");
let name = ModuleName::from_str("mymodule");
assert_eq!(name.to_string(), "mymodule");
}
#[test]
fn test_pch_validation_result_clone() {
let result = X86PCHValidationResult {
is_valid: true,
failure_reason: Some("test".to_string()),
language_options_match: false,
target_options_match: true,
header_search_paths_match: true,
stat_cache_used: true,
duration_ms: 100,
};
let cloned = result.clone();
assert_eq!(cloned.is_valid, result.is_valid);
assert_eq!(cloned.failure_reason, result.failure_reason);
assert_eq!(cloned.duration_ms, result.duration_ms);
}
#[test]
fn test_pch_load_result_clone() {
let result = X86PCHLoadResult {
ast: None,
identifiers: HashMap::new(),
macros: HashMap::new(),
source_files: vec![],
preprocessor_state: None,
bytes_read: 2048,
success: false,
warnings: vec!["warning 1".to_string()],
};
let cloned = result.clone();
assert_eq!(cloned.bytes_read, result.bytes_read);
assert_eq!(cloned.success, result.success);
assert_eq!(cloned.warnings.len(), 1);
}
}