Preliminary call graph definition.
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*~
|
||||
/target
|
||||
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "shatterc"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bumpalo = "3.17"
|
||||
clang = { version = "2.0", features = ["clang_10_0"] }
|
||||
ghost-cell = "0.2.6"
|
||||
193
src/call_graph.rs
Normal file
193
src/call_graph.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use std::collections::{HashMap, HashSet, hash_map};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use ghost_cell::{GhostCell, GhostToken};
|
||||
|
||||
pub struct Typedef<'arena, 'id> {
|
||||
file: FileRef<'arena, 'id>,
|
||||
name: String,
|
||||
target: TypeDefinitionRef<'arena, 'id>,
|
||||
}
|
||||
|
||||
pub type TypedefRef<'arena, 'id> = &'arena GhostCell<'id, Typedef<'arena, 'id>>;
|
||||
|
||||
pub struct StructDefinition<'arena, 'id> {
|
||||
file: FileRef<'arena, 'id>,
|
||||
name: String,
|
||||
types: HashSet<TypeDefinitionRef<'arena, 'id>>,
|
||||
}
|
||||
|
||||
pub type StructDefinitionRef<'arena, 'id> = &'arena StructDefinition<'arena, 'id>;
|
||||
|
||||
pub struct EnumDefinition<'arena, 'id> {
|
||||
file: FileRef<'arena, 'id>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub type EnumDefinitionRef<'arena, 'id> = &'arena EnumDefinition<'arena, 'id>;
|
||||
|
||||
pub enum TypeDefinitionRef<'arena, 'id> {
|
||||
Typedef(TypedefRef<'arena, 'id>),
|
||||
Struct(StructDefinitionRef<'arena, 'id>),
|
||||
Enum(EnumDefinitionRef<'arena, 'id>),
|
||||
}
|
||||
|
||||
pub struct File<'arena, 'id> {
|
||||
path: String,
|
||||
typedefs: HashMap<String, TypedefRef<'arena, 'id>>,
|
||||
structs: HashMap<String, StructDefinitionRef<'arena, 'id>>,
|
||||
enums: HashMap<String, EnumDefinitionRef<'arena, 'id>>,
|
||||
fns: HashMap<String, FunctionDefinitionRef<'arena, 'id>>,
|
||||
}
|
||||
|
||||
impl<'arena, 'id> File<'arena, 'id> {
|
||||
pub fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn typedefs(&self) -> impl Iterator<Item = TypedefRef<'arena, 'id>> + '_ {
|
||||
self.typedefs.values().copied()
|
||||
}
|
||||
|
||||
pub fn structs(&self) -> impl Iterator<Item = StructDefinitionRef<'arena, 'id>> + '_ {
|
||||
self.structs.values().copied()
|
||||
}
|
||||
|
||||
pub fn enums(&self) -> impl Iterator<Item = EnumDefinitionRef<'arena, 'id>> + '_ {
|
||||
self.enums.values().copied()
|
||||
}
|
||||
|
||||
pub fn fns(&self) -> impl Iterator<Item = FunctionDefinitionRef<'arena, 'id>> + '_ {
|
||||
self.fns.values().copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub type FileRef<'arena, 'id> = &'arena GhostCell<'id, File<'arena, 'id>>;
|
||||
|
||||
pub struct FunctionDefinition<'arena, 'id> {
|
||||
public: bool,
|
||||
name: String,
|
||||
file: FileRef<'arena, 'id>,
|
||||
types: HashSet<TypeDefinitionRef<'arena, 'id>>,
|
||||
fn_calls: HashSet<FunctionDefinitionRef<'arena, 'id>>,
|
||||
}
|
||||
|
||||
impl<'arena, 'id> FunctionDefinition<'arena, 'id> {
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.public
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn file(&self) -> &FileRef<'arena, 'id> {
|
||||
&self.file
|
||||
}
|
||||
|
||||
pub fn types(&self) -> impl Iterator<Item = TypeDefinitionRef<'arena, 'id>> + '_ {
|
||||
self.types.iter().copied()
|
||||
}
|
||||
|
||||
pub fn fn_calls(&self) -> impl Iterator<Item = FunctionDefinitionRef<'arena, 'id>> + '_ {
|
||||
self.fn_calls.iter().copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub type FunctionDefinitionRef<'arena, 'id> =
|
||||
&'arena GhostCell<'id, FunctionDefinition<'arena, 'id>>;
|
||||
|
||||
pub struct Graph<'arena, 'id> {
|
||||
arena: &'arena Bump,
|
||||
files: HashMap<String, FileRef<'arena, 'id>>,
|
||||
}
|
||||
|
||||
impl<'arena, 'id> Graph<'arena, 'id> {
|
||||
pub fn find_or_add_file_with_path(&mut self, path: &str) -> FileRef<'arena, 'id> {
|
||||
match self.files.entry(path.into()) {
|
||||
hash_map::Entry::Occupied(o) => *o.get(),
|
||||
hash_map::Entry::Vacant(v) => {
|
||||
let f = GhostCell::from_mut(self.arena.alloc(File {
|
||||
path: String::from(path),
|
||||
typedefs: HashMap::new(),
|
||||
structs: HashMap::new(),
|
||||
enums: HashMap::new(),
|
||||
fns: HashMap::new(),
|
||||
}));
|
||||
v.insert(f);
|
||||
f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_or_add_fn_definition(
|
||||
&mut self,
|
||||
tok: &mut GhostToken<'id>,
|
||||
file: FileRef<'arena, 'id>,
|
||||
public: bool,
|
||||
fn_name: &str,
|
||||
) -> FunctionDefinitionRef<'arena, 'id> {
|
||||
if let Some(existing) = file
|
||||
.borrow(tok)
|
||||
.fns()
|
||||
.find(|f| f.borrow(tok).name() == fn_name)
|
||||
{
|
||||
return *existing;
|
||||
}
|
||||
let def = GhostCell::from_mut(self.arena.alloc(FunctionDefinition {
|
||||
public,
|
||||
name: fn_name.into(),
|
||||
file,
|
||||
types: HashSet::new(),
|
||||
fn_calls: HashSet::new(),
|
||||
}));
|
||||
file.borrow_mut(tok).fns.push(def);
|
||||
def
|
||||
}
|
||||
|
||||
pub fn find_or_add_typedef(
|
||||
&mut self,
|
||||
tok: &mut GhostToken<'id>,
|
||||
file: FileRef<'arena, 'id>,
|
||||
type_name: &str,
|
||||
) -> TypedefRef<'arena, 'id> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn find_or_add_enum_definition(
|
||||
&mut self,
|
||||
tok: &mut GhostToken<'id>,
|
||||
file: FileRef<'arena, 'id>,
|
||||
type_name: &str,
|
||||
) -> EnumDefinitionRef<'arena, 'id> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn find_or_add_struct_definition(
|
||||
&mut self,
|
||||
tok: &mut GhostToken<'id>,
|
||||
file: FileRef<'arena, 'id>,
|
||||
type_name: &str,
|
||||
) -> StructDefinitionRef<'arena, 'id> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn files(&self) -> impl Iterator<Item = FileRef<'arena, 'id>> + '_ {
|
||||
self.files.values().copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<R>(
|
||||
arena: &Bump,
|
||||
f: impl for<'arena, 'id> FnOnce(GhostToken<'id>, Graph<'arena, 'id>) -> R,
|
||||
) -> R {
|
||||
GhostToken::new(|t| {
|
||||
f(
|
||||
t,
|
||||
Graph {
|
||||
arena,
|
||||
files: HashMap::new(),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
8
src/discover.rs
Normal file
8
src/discover.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use crate::call_graph::{FileRef, FunctionDefinitionRef, Graph};
|
||||
|
||||
pub fn function_body<'arena, 'id>(
|
||||
graph: &mut Graph<'arena, 'id>,
|
||||
file: FileRef<'arena, 'id>,
|
||||
) -> FunctionDefinitionRef<'arena, 'id> {
|
||||
unimplemented!()
|
||||
}
|
||||
117
src/main.rs
Normal file
117
src/main.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use clang::{Clang, EntityKind, Index, StorageClass};
|
||||
use std::{
|
||||
collections::{VecDeque, hash_map::Entry},
|
||||
env,
|
||||
};
|
||||
|
||||
mod call_graph;
|
||||
mod discover;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let files: Vec<_> = env::args().skip(1).collect();
|
||||
|
||||
let clang = Clang::new()?;
|
||||
let index = Index::new(&clang, /*exclude=*/ false, /*diagnostics=*/ false);
|
||||
|
||||
let ignore_functions = vec![
|
||||
format!("__bswap_16"),
|
||||
format!("__bswap_32"),
|
||||
format!("__bswap_64"),
|
||||
format!("__uint16_identity"),
|
||||
format!("__uint32_identity"),
|
||||
format!("__uint64_identity"),
|
||||
];
|
||||
|
||||
let arena = bumpalo::Bump::new();
|
||||
call_graph::new(&arena, |tok, graph| {
|
||||
// Parse first, ask questions later.
|
||||
let mut parsed = Vec::with_capacity(files.len());
|
||||
for file in &files {
|
||||
match index.parse(file).parse() {
|
||||
Ok(parsed) => parsed.push((graph.find_or_add_file_with_path(file), parsed)),
|
||||
Err(_) => eprintln!("could not parse file: `{}`", file),
|
||||
}
|
||||
}
|
||||
let parsed = parsed;
|
||||
|
||||
// First pass: discover function, struct, enum, and typedef definitions.
|
||||
let typedefs = HashMap::new();
|
||||
let structs = HashMap::new();
|
||||
let enums = HashMap::new();
|
||||
let fns = HashMap::new();
|
||||
for (file, parsed) in &parsed {
|
||||
for child in parsed.get_entity().get_children().into_iter() {
|
||||
if let Some(def) = child.get_definition() {
|
||||
match child.get_kind() {
|
||||
EntityKind::FunctionDecl => unimplemented!(),
|
||||
EntityKind::StructDecl => unimplemented!(),
|
||||
EntityKind::EnumDecl => unimplemented!(),
|
||||
EntityKind::TypedefDecl => unimplemented!(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: descend into definitions.
|
||||
for (typedef_vertex, def) in typedefs.into_iter() {
|
||||
unimplemented!()
|
||||
}
|
||||
for (struct_vertex, def) in structs.into_iter() {
|
||||
unimplemented!()
|
||||
}
|
||||
for (enum_vertex, def) in enums.into_iter() {
|
||||
unimplemented!()
|
||||
}
|
||||
for (fn_vertex, def) in fns.into_iter() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// if let Some(def) = fn_decl.get_definition() {
|
||||
// if let Some(decl_name) = fn_decl.get_name() {
|
||||
// if ignore_functions.contains(&decl_name) {
|
||||
// continue;
|
||||
// }
|
||||
// let fn_decl = graph.find_or_add_fn_definition(
|
||||
// &mut tok,
|
||||
// infile,
|
||||
// /*public=*/
|
||||
// fn_decl.get_storage_class() == Some(StorageClass::Static),
|
||||
// /*fn_name=*/ &decl_name,
|
||||
// );
|
||||
|
||||
// let mut frontier = VecDeque::from(def.get_children());
|
||||
// while let Some(e) = frontier.pop_front() {
|
||||
// match e.get_kind() {
|
||||
// EntityKind::CallExpr => {
|
||||
// if let Some(called_name) = e.get_name() {
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if e.get_kind() == EntityKind::CallExpr {
|
||||
// if let Some(called_name) = e.get_name() {
|
||||
// match caller2called.entry(decl.name().into()) {
|
||||
// Entry::Occupied(mut o) => o.get_mut().push(called_name),
|
||||
// Entry::Vacant(v) => {
|
||||
// v.insert(vec![called_name]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for c in e.get_children().into_iter() {
|
||||
// frontier.push_back(c);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// return Err(format!(
|
||||
// "function definition in {} has no name",
|
||||
// infile.borrow(&tok).path()
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user