1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#![allow(non_camel_case_types)]
extern crate libc;
extern crate serde_bytes;
extern crate serde_cbor;

use serde_cbor::{from_slice, Value};
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::io::{Error, ErrorKind};
use std::path::Path;
use std::slice;

pub mod clang_ast;

pub fn get_clang_major_version() -> Option<u32> {
    let s = unsafe { CStr::from_ptr(clang_version()) };
    s.to_str()
        .unwrap()
        .split('.')
        .next()
        .unwrap()
        .parse::<u32>()
        .ok()
}

pub fn get_untyped_ast(
    file_path: &Path,
    cc_db: &Path,
    extra_args: &[&str],
) -> Result<clang_ast::AstContext, Error> {
    let cbors = get_ast_cbors(file_path, cc_db, extra_args);
    let buffer = cbors.values().next().ok_or(Error::new(
        ErrorKind::InvalidData,
        "Could not parse input file",
    ))?;

    // let cbor_path = file_path.with_extension("cbor");
    // let mut cbor_file = File::create(&cbor_path)?;
    // cbor_file.write_all(&buffer[..])?;
    // eprintln!("Dumped CBOR to {}", cbor_path.to_string_lossy());

    let items: Value = from_slice(&buffer[..]).unwrap();

    match clang_ast::process(items) {
        Ok(cxt) => Ok(cxt),
        Err(e) => Err(Error::new(ErrorKind::InvalidData, format!("{:}", e))),
    }
}

fn get_ast_cbors(file_path: &Path, cc_db: &Path, extra_args: &[&str]) -> HashMap<String, Vec<u8>> {
    let mut res = 0;

    let mut args_owned = vec![CString::new("ast_exporter").unwrap()];
    args_owned.push(CString::new(file_path.to_str().unwrap()).unwrap());
    args_owned.push(CString::new("-p").unwrap());
    args_owned.push(CString::new(cc_db.to_str().unwrap()).unwrap());

    for &arg in extra_args {
        args_owned.push(CString::new(["-extra-arg=", arg].join("")).unwrap())
    }

    let args_ptrs: Vec<*const libc::c_char> = args_owned.iter().map(|x| x.as_ptr()).collect();

    let hashmap;
    unsafe {
        let ptr = ast_exporter(args_ptrs.len() as libc::c_int, args_ptrs.as_ptr(), &mut res);
        hashmap = marshal_result(ptr);
        drop_export_result(ptr);
    }
    hashmap
}

include!(concat!(env!("OUT_DIR"), "/cppbindings.rs"));

extern "C" {
    // ExportResult *ast_exporter(int argc, char *argv[]);
    #[no_mangle]
    fn ast_exporter(
        argc: libc::c_int,
        argv: *const *const libc::c_char,
        res: *mut libc::c_int,
    ) -> *mut ExportResult;

    // void drop_export_result(ExportResult *result);
    #[no_mangle]
    fn drop_export_result(ptr: *mut ExportResult);

    #[no_mangle]
    fn clang_version() -> *const libc::c_char;
}

unsafe fn marshal_result(result: *const ExportResult) -> HashMap<String, Vec<u8>> {
    let mut output = HashMap::new();

    let n = (*result).entries as isize;
    for i in 0..n {
        let ref res = *result;

        // Convert name field
        let cname = CStr::from_ptr(*res.names.offset(i));
        let name = cname.to_str().unwrap().to_owned();

        // Convert CBOR bytes
        let csize = *res.sizes.offset(i);
        let cbytes = *res.bytes.offset(i);
        let bytes = slice::from_raw_parts(cbytes, csize);
        let mut v = Vec::new();
        v.extend_from_slice(bytes);

        output.insert(name, v);
    }
    output
}