Skip to main content

c2rust_ast_exporter/
lib.rs

1use serde_cbor::{from_slice, Value};
2use std::collections::HashMap;
3use std::ffi::{c_char, c_int, CStr, CString};
4use std::io::{Error, ErrorKind};
5use std::path::Path;
6use std::slice;
7
8use crate::clang_ast::BuiltinVaListKind;
9
10pub mod clang_ast;
11
12impl Default for BuiltinVaListKind {
13    fn default() -> Self {
14        Self::CharPtrBuiltinVaList
15    }
16}
17
18pub fn get_clang_major_version() -> Option<u32> {
19    let s = unsafe { CStr::from_ptr(clang_version()) };
20    s.to_str()
21        .unwrap()
22        .split('.')
23        .next()
24        .unwrap()
25        .parse::<u32>()
26        .ok()
27}
28
29pub fn get_untyped_ast(
30    file_path: &Path,
31    cc_db: &Path,
32    extra_args: &[&str],
33    debug: bool,
34) -> Result<clang_ast::AstContext, Error> {
35    let cbors = get_ast_cbors(file_path, cc_db, extra_args, debug);
36    let buffer = cbors
37        .values()
38        .next()
39        .ok_or_else(|| Error::new(ErrorKind::InvalidData, "Could not parse input file"))?;
40
41    // let cbor_path = file_path.with_extension("cbor");
42    // let mut cbor_file = File::create(&cbor_path)?;
43    // cbor_file.write_all(&buffer[..])?;
44    // eprintln!("Dumped CBOR to {}", cbor_path.to_string_lossy());
45
46    let items: Value = from_slice(&buffer[..]).unwrap();
47
48    clang_ast::process(items).map_err(|e| Error::new(ErrorKind::InvalidData, format!("{}", e)))
49}
50
51fn get_ast_cbors(
52    file_path: &Path,
53    cc_db: &Path,
54    extra_args: &[&str],
55    debug: bool,
56) -> HashMap<String, Vec<u8>> {
57    let mut res = 0;
58
59    let mut args_owned = vec![CString::new("ast_exporter").unwrap()];
60    args_owned.push(CString::new(file_path.to_str().unwrap()).unwrap());
61    args_owned.push(CString::new("-p").unwrap());
62    args_owned.push(CString::new(cc_db.to_str().unwrap()).unwrap());
63
64    for &arg in extra_args {
65        args_owned.push(CString::new(["-extra-arg=", arg].join("")).unwrap())
66    }
67
68    let args_ptrs: Vec<*const c_char> = args_owned.iter().map(|x| x.as_ptr()).collect();
69
70    let hashmap;
71    unsafe {
72        let ptr = ast_exporter(
73            args_ptrs.len() as c_int,
74            args_ptrs.as_ptr(),
75            debug.into(),
76            &mut res,
77        );
78        hashmap = marshal_result(ptr);
79        drop_export_result(ptr);
80    }
81    hashmap
82}
83
84#[allow(non_camel_case_types)]
85#[allow(non_snake_case)]
86#[allow(dead_code)]
87mod ffi {
88    include!(concat!(env!("OUT_DIR"), "/cppbindings.rs"));
89}
90
91extern "C" {
92    // ExportResult *ast_exporter(int argc, char *argv[]);
93    fn ast_exporter(
94        argc: c_int,
95        argv: *const *const c_char,
96        debug: c_int,
97        res: *mut c_int,
98    ) -> *mut ffi::ExportResult;
99
100    // void drop_export_result(ExportResult *result);
101    fn drop_export_result(ptr: *mut ffi::ExportResult);
102
103    fn clang_version() -> *const c_char;
104}
105
106unsafe fn marshal_result(result: *const ffi::ExportResult) -> HashMap<String, Vec<u8>> {
107    let mut output = HashMap::new();
108
109    let n = (*result).entries as isize;
110    for i in 0..n {
111        let res = &*result;
112
113        // Convert name field
114        let cname = CStr::from_ptr(*res.names.offset(i));
115        let name = cname.to_str().unwrap().to_owned();
116
117        // Convert CBOR bytes
118        let csize = *res.sizes.offset(i);
119        let cbytes = *res.bytes.offset(i);
120        #[allow(clippy::unnecessary_cast /*, reason = "needed on x86_64-unknown-linux-gnu" */)]
121        let bytes = slice::from_raw_parts(cbytes, csize as usize);
122        let mut v = Vec::new();
123        v.extend_from_slice(bytes);
124
125        output.insert(name, v);
126    }
127    output
128}