#![allow(non_upper_case_globals)]
use std::collections::{HashMap, HashSet};
use std::collections::hash_map;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use clang_sys::*;
use syntax::abi;
use types as il;
use types::*;
use clang as cx;
use clang::{Cursor, Diagnostic, TranslationUnit, ast_dump};
use super::Logger;
pub struct MacroTypes {
pub t_u8: IKind,
pub t_u16: IKind,
pub t_u32: IKind,
pub t_u64: IKind,
pub t_i8: IKind,
pub t_i16: IKind,
pub t_i32: IKind,
pub t_i64: IKind,
}
pub struct ClangParserOptions {
pub builtin_names: HashSet<String>,
pub builtins: bool,
pub match_pat: Vec<String>,
pub emit_ast: bool,
pub fail_on_unknown_type: bool,
pub override_enum_ty: Option<il::IKind>,
pub clang_args: Vec<String>,
pub macros: bool,
pub macro_types: MacroTypes,
}
struct ClangParserCtx<'a> {
options: ClangParserOptions,
name: HashMap<Cursor, Global>,
globals: Vec<Global>,
builtin_defs: Vec<Cursor>,
defined_macros: HashMap<Vec<u8>,::cexpr::expr::EvalResult>,
logger: &'a (Logger + 'a),
err_count: i32,
}
fn match_pattern(ctx: &mut ClangParserCtx, cursor: &Cursor) -> bool {
let (file, _, _, _) = cursor.location().location();
let name = match file.name() {
None => return ctx.options.builtins,
Some(name) => name,
};
if ctx.options.match_pat.is_empty() {
return true;
}
let mut found = false;
ctx.options.match_pat.iter().all(|pat| {
if (&name[..]).contains(pat) {
found = true;
}
true
});
found
}
#[cfg_attr(feature = "clippy", allow(match_same_arms))]
fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global {
let cursor = cursor.canonical();
let mut new_decl = false;
let override_enum_ty = ctx.options.override_enum_ty;
let decl = match ctx.name.entry(cursor) {
hash_map::Entry::Occupied(ref e) => e.get().clone(),
hash_map::Entry::Vacant(e) => {
new_decl = true;
let spelling = cursor.spelling();
let ty = cursor.cur_type();
let layout = Layout::new(ty.size(), ty.align());
debug!("type `{}` = {:?}; // {:?}", spelling, ty, layout);
let glob_decl = match cursor.kind() {
CXCursorKind::StructDecl => {
let ci = Rc::new(RefCell::new(CompInfo::new(spelling,
CompKind::Struct,
vec![],
layout)));
GCompDecl(ci)
}
CXCursorKind::UnionDecl => {
let ci = Rc::new(RefCell::new(CompInfo::new(spelling,
CompKind::Union,
vec![],
layout)));
GCompDecl(ci)
}
CXCursorKind::EnumDecl => {
let kind = match override_enum_ty {
Some(t) => t,
None => {
match cursor.enum_type().kind() {
CXTypeKind::SChar | CXTypeKind::Char_S => ISChar,
CXTypeKind::UChar | CXTypeKind::Char_U => IUChar,
CXTypeKind::UShort => IUShort,
CXTypeKind::UInt => IUInt,
CXTypeKind::ULong => IULong,
CXTypeKind::ULongLong => IULongLong,
CXTypeKind::Short => IShort,
CXTypeKind::Int => IInt,
CXTypeKind::Long => ILong,
CXTypeKind::LongLong => ILongLong,
_ => IInt,
}
}
};
let ei = Rc::new(RefCell::new(EnumInfo::new(spelling, kind, vec![], layout)));
GEnumDecl(ei)
}
CXCursorKind::TypedefDecl => {
let ti = Rc::new(RefCell::new(TypeInfo::new(spelling, TVoid, layout)));
GType(ti)
}
CXCursorKind::VarDecl => {
let vi = Rc::new(RefCell::new(VarInfo::new(spelling, TVoid)));
GVar(vi)
}
CXCursorKind::FunctionDecl => {
let vi = Rc::new(RefCell::new(VarInfo::new(spelling, TVoid)));
GFunc(vi)
}
_ => GOther,
};
e.insert(glob_decl.clone());
glob_decl
}
};
if new_decl && ctx.options.builtin_names.contains(&cursor.spelling()) {
ctx.builtin_defs.push(cursor);
}
decl
}
fn opaque_decl(ctx: &mut ClangParserCtx, decl: &Cursor) {
let name = decl_name(ctx, decl);
ctx.globals.push(name);
}
fn fwd_decl<F: FnOnce(&mut ClangParserCtx) -> ()>(ctx: &mut ClangParserCtx,
cursor: &Cursor,
f: F) {
let def = &cursor.definition();
if cursor == def {
f(ctx);
} else if def.kind() == CXCursorKind::NoDeclFound || def.kind() == CXCursorKind::InvalidFile {
opaque_decl(ctx, cursor);
}
}
fn get_abi(cc: CXCallingConv) -> abi::Abi {
match cc {
CXCallingConv::Default | CXCallingConv::C => abi::Abi::C,
CXCallingConv::X86StdCall => abi::Abi::Stdcall,
CXCallingConv::X86FastCall => abi::Abi::Fastcall,
CXCallingConv::AAPCS => abi::Abi::Aapcs,
CXCallingConv::X86_64Win64 => abi::Abi::Win64,
other => panic!("unsupported calling convention: {:?}", other),
}
}
fn conv_ptr_ty(ctx: &mut ClangParserCtx,
ty: &cx::Type,
cursor: &Cursor,
layout: Layout)
-> il::Type {
let is_const = ty.is_const();
match ty.kind() {
CXTypeKind::Unexposed |
CXTypeKind::FunctionProto |
CXTypeKind::FunctionNoProto => {
let ret_ty = ty.ret_type();
let decl = ty.declaration();
if ret_ty.kind() != CXTypeKind::Invalid {
TFuncPtr(mk_fn_sig(ctx, ty, cursor), layout)
} else if decl.kind() != CXCursorKind::NoDeclFound {
TPtr(Box::new(conv_decl_ty(ctx, &decl)), ty.is_const(), layout)
} else if cursor.kind() == CXCursorKind::VarDecl {
let can_ty = ty.canonical_type();
conv_ty(ctx, &can_ty, cursor)
} else {
TPtr(Box::new(TVoid), ty.is_const(), layout)
}
}
_ => TPtr(Box::new(conv_ty(ctx, ty, cursor)), is_const, layout),
}
}
fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::FuncSig {
let args_lst: Vec<(String, il::Type)> = match cursor.kind() {
CXCursorKind::FunctionDecl => {
cursor.args()
.iter()
.map(|arg| {
let arg_name = arg.spelling();
(arg_name, conv_ty(ctx, &arg.cur_type(), arg))
})
.collect()
}
_ => {
let mut args_lst = vec![];
cursor.visit(|c: &Cursor, _: &Cursor| {
if c.kind() == CXCursorKind::ParmDecl {
args_lst.push((c.spelling(), conv_ty(ctx, &c.cur_type(), c)));
}
CXChildVisitResult::Continue
});
args_lst
}
};
let ret_ty = Box::new(conv_ty(ctx, &ty.ret_type(), cursor));
let abi = get_abi(ty.call_conv());
let is_unsafe = args_lst.iter().any(|arg| {
match arg.1 {
TPtr(_, _, _) => true,
_ => false,
}
});
il::FuncSig {
ret_ty: ret_ty,
args: args_lst,
is_variadic: ty.is_variadic(),
is_safe: !is_unsafe,
abi: abi,
}
}
fn conv_decl_ty(ctx: &mut ClangParserCtx, cursor: &Cursor) -> il::Type {
match cursor.kind() {
CXCursorKind::StructDecl | CXCursorKind::UnionDecl => {
let decl = decl_name(ctx, cursor);
let ci = decl.compinfo();
TComp(ci)
}
CXCursorKind::EnumDecl => {
let decl = decl_name(ctx, cursor);
let ei = decl.enuminfo();
TEnum(ei)
}
CXCursorKind::TypedefDecl => {
let decl = decl_name(ctx, cursor);
let ti = decl.typeinfo();
TNamed(ti)
}
_ => TVoid,
}
}
fn conv_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type {
debug!("conv_ty: ty=`{:?}` sp=`{}` loc=`{}` size=`{}` align=`{}`",
ty.kind(),
cursor.spelling(),
cursor.location(),
ty.size(),
ty.align());
let layout = Layout::new(ty.size(), ty.align());
match ty.kind() {
CXTypeKind::Void | CXTypeKind::Invalid => TVoid,
CXTypeKind::Bool => TInt(IBool, layout),
CXTypeKind::SChar |
CXTypeKind::Char_S => TInt(ISChar, layout),
CXTypeKind::UChar |
CXTypeKind::Char_U => TInt(IUChar, layout),
CXTypeKind::UShort => TInt(IUShort, layout),
CXTypeKind::UInt => TInt(IUInt, layout),
CXTypeKind::ULong => TInt(IULong, layout),
CXTypeKind::ULongLong => TInt(IULongLong, layout),
CXTypeKind::Short => TInt(IShort, layout),
CXTypeKind::Int => TInt(IInt, layout),
CXTypeKind::Long => TInt(ILong, layout),
CXTypeKind::LongLong => TInt(ILongLong, layout),
CXTypeKind::WChar => TInt(IWChar, layout),
CXTypeKind::Float => TFloat(FFloat, layout),
CXTypeKind::Double | CXTypeKind::LongDouble => TFloat(FDouble, layout),
CXTypeKind::Pointer => conv_ptr_ty(ctx, &ty.pointee_type(), cursor, layout),
CXTypeKind::VariableArray => unreachable!(),
CXTypeKind::DependentSizedArray | CXTypeKind::IncompleteArray => {
TArray(Box::new(conv_ty(ctx, &ty.elem_type(), cursor)), 0, layout)
}
CXTypeKind::FunctionProto | CXTypeKind::FunctionNoProto => {
TFuncProto(mk_fn_sig(ctx, ty, cursor), layout)
}
CXTypeKind::Record |
CXTypeKind::Typedef |
CXTypeKind::Unexposed |
CXTypeKind::Enum => conv_decl_ty(ctx, &ty.declaration()),
CXTypeKind::ConstantArray => {
TArray(Box::new(conv_ty(ctx, &ty.elem_type(), cursor)),
ty.array_size(),
layout)
}
CXTypeKind::Vector => {
log_err_warn(ctx,
"Vector types are not supported, see https://github.com/crabtw/rust-bindgen/issues/356",
true);
TVoid
}
CXTypeKind::Int128 | CXTypeKind::UInt128 => {
log_err_warn(ctx,
"128-bits integers are not supported, see https://github.com/crabtw/rust-bindgen/issues/355",
true);
TVoid
}
_ => {
let fail = ctx.options.fail_on_unknown_type;
log_err_warn(ctx,
&format!("unsupported type `{:?}` ({})",
ty.kind(),
cursor.location())[..],
fail);
TVoid
}
}
}
fn opaque_ty(ctx: &mut ClangParserCtx, ty: &cx::Type) {
if ty.kind() == CXTypeKind::Record || ty.kind() == CXTypeKind::Enum {
let decl = ty.declaration();
let def = decl.definition();
if def.kind() == CXCursorKind::NoDeclFound || def.kind() == CXCursorKind::InvalidFile {
opaque_decl(ctx, &decl);
}
}
}
impl MacroTypes {
fn infer(&self, val: &::cexpr::expr::EvalResult) -> Option<(Type,Option<i64>)> {
use cexpr::expr::EvalResult::*;
match val {
&Int(::std::num::Wrapping(i)) => {
let kind=if i>=0 {
if i<=(::std::u8::MAX as i64) {
self.t_u8
} else if i<=(::std::u16::MAX as i64) {
self.t_u16
} else if i<=(::std::u32::MAX as i64) {
self.t_u32
} else {
self.t_u64
}
} else {
if i>=(::std::i8::MIN as i64) {
self.t_i8
} else if i>=(::std::i16::MIN as i64) {
self.t_i16
} else if i>=(::std::i32::MIN as i64) {
self.t_i32
} else {
self.t_i64
}
};
Some((Type::TInt(kind,Layout::default()),Some(i)))
},
_ => None,
}
}
}
fn visit_composite(cursor: &Cursor,
parent: &Cursor,
ctx: &mut ClangParserCtx,
compinfo: &mut CompInfo)
-> CXChildVisitResult {
fn is_bitfield_continuation(field: &il::FieldInfo, ty: &il::Type, width: u32) -> bool {
match (&field.bitfields, ty) {
(&Some(ref bitfields), &il::TInt(_, layout)) if *ty == field.ty => {
bitfields.iter().map(|&(_, w)| w).fold(0u32, |acc, w| acc + w) + width <=
(layout.size * 8) as u32
}
_ => false,
}
}
fn inner_composite(mut ty: &il::Type) -> Option<&Rc<RefCell<CompInfo>>> {
loop {
match *ty {
TComp(ref comp_ty) => return Some(comp_ty),
TPtr(ref ptr_ty, _, _) => ty = &**ptr_ty,
TArray(ref array_ty, _, _) => ty = &**array_ty,
_ => return None,
}
}
}
fn inner_enumeration(mut ty: &il::Type) -> Option<&Rc<RefCell<EnumInfo>>> {
loop {
match *ty {
TEnum(ref enum_ty) => return Some(enum_ty),
TPtr(ref ptr_ty, _, _) => ty = &**ptr_ty,
TArray(ref array_ty, _, _) => ty = &**array_ty,
_ => return None,
}
}
}
let members = &mut compinfo.members;
match cursor.kind() {
CXCursorKind::FieldDecl => {
let ty = conv_ty(ctx, &cursor.cur_type(), cursor);
let (name, bitfields) = match (cursor.bit_width(), members.last_mut()) {
(Some(width), Some(&mut il::CompMember::Field(ref mut field)))
if is_bitfield_continuation(field, &ty, width) => {
if let Some(ref mut bitfields) = field.bitfields {
bitfields.push((cursor.spelling(), width));
} else {
unreachable!()
}
return CXChildVisitResult::Continue;
}
(Some(width), _) => {
match ty {
il::TInt(_, _) => (),
_ => {
let msg = format!("Enums in bitfields are not supported ({}.{}).",
cursor.spelling(),
parent.spelling());
ctx.logger.warn(&msg[..]);
}
}
("".to_owned(), Some(vec![(cursor.spelling(), width)]))
}
(None, _) => (cursor.spelling(), None),
};
let is_composite = match (inner_composite(&ty), members.last()) {
(Some(ty_compinfo), Some(&CompMember::Comp(ref c))) => {
c.borrow().deref() as *const _ == ty_compinfo.borrow().deref() as *const _
}
_ => false,
};
let is_enumeration = match (inner_enumeration(&ty), members.last()) {
(Some(ty_enuminfo), Some(&CompMember::Enum(ref e))) => {
e.borrow().deref() as *const _ == ty_enuminfo.borrow().deref() as *const _
}
_ => false,
};
let field = FieldInfo::new(name, ty.clone(), bitfields);
if is_composite {
if let Some(CompMember::Comp(c)) = members.pop() {
members.push(CompMember::CompField(c, field));
} else {
unreachable!(); }
} else if is_enumeration {
if let Some(CompMember::Enum(e)) = members.pop() {
members.push(CompMember::EnumField(e, field));
} else {
unreachable!(); }
} else {
members.push(CompMember::Field(field));
}
}
CXCursorKind::StructDecl | CXCursorKind::UnionDecl => {
fwd_decl(ctx, cursor, |ctx_| {
let decl = decl_name(ctx_, cursor);
let ci = decl.compinfo();
cursor.visit(|c, p| {
let mut ci_ = ci.borrow_mut();
visit_composite(c, p, ctx_, &mut ci_)
});
members.push(CompMember::Comp(decl.compinfo()));
});
}
CXCursorKind::EnumDecl => {
fwd_decl(ctx, cursor, |ctx_| {
let decl = decl_name(ctx_, cursor);
let ci = decl.enuminfo();
cursor.visit(|c, _| {
let mut ci_ = ci.borrow_mut();
visit_enum(c, &mut ci_.items)
});
members.push(CompMember::Enum(decl.enuminfo()));
});
}
CXCursorKind::PackedAttr => {
compinfo.layout.packed = true;
}
CXCursorKind::UnexposedAttr => {
}
_ => {
log_err_warn(ctx,
&format!("unhandled composite member `{}` (kind {:?}) in `{}` ({})",
cursor.spelling(),
cursor.kind(),
parent.spelling(),
cursor.location())[..],
false);
}
}
CXChildVisitResult::Continue
}
fn visit_enum(cursor: &Cursor, items: &mut Vec<EnumItem>) -> CXChildVisitResult {
if cursor.kind() == CXCursorKind::EnumConstantDecl {
let name = cursor.spelling();
let val = cursor.enum_val();
let item = EnumItem::new(name, val);
items.push(item);
}
CXChildVisitResult::Continue
}
fn visit_literal(cursor: &Cursor, unit: &TranslationUnit) -> Option<i64> {
if cursor.kind() == CXCursorKind::IntegerLiteral {
match unit.tokens(cursor) {
None => None,
Some(tokens) => {
if tokens.is_empty() || tokens[0].kind != CXTokenKind::Literal {
None
} else {
let s = &tokens[0].spelling;
let parsed = {
if s.starts_with("0x") {
i64::from_str_radix(&s[2..], 16)
} else {
s.parse()
}
};
match parsed {
Ok(i) => Some(i),
Err(_) => None,
}
}
}
}
} else {
None
}
}
fn visit_top(cursor: &Cursor,
ctx: &mut ClangParserCtx,
unit: &TranslationUnit)
-> CXChildVisitResult {
if !match_pattern(ctx, cursor) {
return CXChildVisitResult::Continue;
}
match cursor.kind() {
CXCursorKind::UnexposedDecl => CXChildVisitResult::Recurse,
CXCursorKind::StructDecl | CXCursorKind::UnionDecl => {
fwd_decl(ctx, cursor, |ctx_| {
let decl = decl_name(ctx_, cursor);
let ci = decl.compinfo();
cursor.visit(|c, p| {
let mut ci_ = ci.borrow_mut();
visit_composite(c, p, ctx_, &mut ci_)
});
ctx_.globals.push(GComp(ci));
});
CXChildVisitResult::Continue
}
CXCursorKind::EnumDecl => {
fwd_decl(ctx, cursor, |ctx_| {
let decl = decl_name(ctx_, cursor);
let ei = decl.enuminfo();
cursor.visit(|c, _: &Cursor| {
let mut ei_ = ei.borrow_mut();
visit_enum(c, &mut ei_.items)
});
ctx_.globals.push(GEnum(ei));
});
CXChildVisitResult::Continue
}
CXCursorKind::FunctionDecl => {
let linkage = cursor.linkage();
if linkage != CXLinkageKind::External && linkage != CXLinkageKind::UniqueExternal {
return CXChildVisitResult::Continue;
}
let func = decl_name(ctx, cursor);
let vi = func.varinfo();
let mut vi = vi.borrow_mut();
let ty = cursor.cur_type();
let layout = Layout::new(ty.size(), ty.align());
vi.ty = TFuncPtr(mk_fn_sig(ctx, &ty, cursor), layout);
ctx.globals.push(func);
CXChildVisitResult::Continue
}
CXCursorKind::VarDecl => {
let linkage = cursor.linkage();
if linkage != CXLinkageKind::External && linkage != CXLinkageKind::UniqueExternal {
return CXChildVisitResult::Continue;
}
let ty = conv_ty(ctx, &cursor.cur_type(), cursor);
let var = decl_name(ctx, cursor);
let vi = var.varinfo();
let mut vi = vi.borrow_mut();
vi.ty = ty.clone();
vi.is_const = cursor.cur_type().is_const();
cursor.visit(|c, _: &Cursor| {
vi.val = visit_literal(c, unit);
CXChildVisitResult::Continue
});
ctx.globals.push(var);
CXChildVisitResult::Continue
}
CXCursorKind::TypedefDecl => {
let mut under_ty = cursor.typedef_type();
if under_ty.kind() == CXTypeKind::Unexposed {
under_ty = under_ty.canonical_type();
}
let ty = conv_ty(ctx, &under_ty, cursor);
let typedef = decl_name(ctx, cursor);
let ti = typedef.typeinfo();
let mut ti = ti.borrow_mut();
ti.ty = ty.clone();
ctx.globals.push(typedef);
opaque_ty(ctx, &under_ty);
CXChildVisitResult::Continue
}
CXCursorKind::MacroDefinition if ctx.options.macros => {
use cexpr::*;
let tokens: Vec<_>=unit.tokens(cursor).unwrap().into_iter().filter_map(|t|
if t.kind!=CXTokenKind::Comment {
Some(t.into())
} else {
None
}
).collect();
match expr::IdentifierParser::new(&ctx.defined_macros).macro_definition(&tokens) {
nom::IResult::Done(_,(id,val)) => {
let id=id.to_owned();
if let Some((ty,val))=ctx.options.macro_types.infer(&val) {
let var=Global::GVar(Rc::new(RefCell::new(VarInfo{
name:String::from_utf8(id.clone()).expect("C identifiers should be valid UTF-8"),
ty:ty,
val:val,
is_const:true,
})));
ctx.globals.push(var);
}
ctx.defined_macros.insert(id,val);
},
_ => {}
}
CXChildVisitResult::Continue
}
_ => CXChildVisitResult::Continue,
}
}
fn log_err_warn(ctx: &mut ClangParserCtx, msg: &str, is_err: bool) {
if is_err {
ctx.err_count += 1;
ctx.logger.error(msg)
} else {
ctx.logger.warn(msg)
}
}
pub fn parse(options: ClangParserOptions, logger: &Logger) -> Result<Vec<Global>, ()> {
let mut ctx = ClangParserCtx {
options: options,
name: HashMap::new(),
builtin_defs: vec![],
globals: vec![],
defined_macros: HashMap::new(),
logger: logger,
err_count: 0,
};
let ix = cx::Index::create(false, true);
if ix.is_null() {
ctx.logger.error("Clang failed to create index");
return Err(());
}
let flags = if ctx.options.macros {
CXTranslationUnit_DetailedPreprocessingRecord
} else {
CXTranslationUnit_Flags::empty()
};
let unit = TranslationUnit::parse(&ix, "", &ctx.options.clang_args[..], &[], flags);
if unit.is_null() {
ctx.logger.error("Unknown parsing error");
return Err(());
}
let diags = unit.diags();
for d in &diags {
let msg = d.format(Diagnostic::default_opts());
let is_err = d.severity() >= CXDiagnosticSeverity::Error;
log_err_warn(&mut ctx, &msg[..], is_err);
}
if ctx.err_count > 0 {
return Err(());
}
let cursor = unit.cursor();
if ctx.options.emit_ast {
cursor.visit(|cur, _: &Cursor| ast_dump(cur, 0));
}
cursor.visit(|cur, _: &Cursor| visit_top(cur, &mut ctx, &unit));
while !ctx.builtin_defs.is_empty() {
let c = ctx.builtin_defs.remove(0);
visit_top(&c.definition(), &mut ctx, &unit);
}
unit.dispose();
ix.dispose();
if ctx.err_count > 0 {
return Err(());
}
Ok(ctx.globals)
}