Skip to content

Commit

Permalink
extract unexposed attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
flier committed Feb 7, 2017
1 parent 7fa654c commit ba747cd
Show file tree
Hide file tree
Showing 15 changed files with 477 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ impl Drop for Index {
}

/// A token emitted by clang's lexer.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Token {
/// The kind of token this is.
pub kind: CXTokenKind,
Expand Down
43 changes: 43 additions & 0 deletions src/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,25 @@ pub mod attributes {
aster::AstBuilder::new().attr().word("inline")
}

pub fn cold() -> ast::Attribute {
aster::AstBuilder::new().attr().word("cold")
}

pub fn deprecated(_: Option<&str>) -> ast::Attribute {
aster::AstBuilder::new().attr().word("deprecated")
}

pub fn doc(comment: &str) -> ast::Attribute {
aster::AstBuilder::new().attr().doc(comment)
}

pub fn link_name(name: &str) -> ast::Attribute {
aster::AstBuilder::new().attr().name_value("link_name").str(name)
}

pub fn allow(rules: Vec<&str>) -> ast::Attribute {
aster::AstBuilder::new().attr().allow(rules)
}
}

/// Generates a proper type for a field or type with a given `Layout`, that is,
Expand Down Expand Up @@ -75,12 +87,15 @@ impl BlobTyBuilder {
}

pub mod ast_ty {
use std::str;
use aster;
use ir::context::BindgenContext;
use ir::function::FunctionSig;
use ir::ty::FloatKind;
use ir::attr;
use syntax::ast;
use syntax::ptr::P;
use super::attributes;

pub fn raw_type(ctx: &BindgenContext, name: &str) -> P<ast::Ty> {
let ident = ctx.rust_ident_raw(&name);
Expand Down Expand Up @@ -187,4 +202,32 @@ pub mod ast_ty {
})
.collect::<Vec<_>>()
}

pub fn attribute_to_rust(attribute: &attr::Attribute) -> ast::Attribute {
match *attribute {
attr::Attribute::Deprecated(ref text) => attributes::deprecated(text.as_ref().map(|s| s.as_str())),
attr::Attribute::Unused => attributes::allow(vec!["dead_code"]),
attr::Attribute::Used => attributes::doc("#[doc = \"__attribute__(unused)\"]"),
attr::Attribute::Cold => attributes::cold(),
attr::Attribute::Const => attributes::doc("#[doc = \"__attribute__(const)\"]"),
attr::Attribute::Constructor(_) => attributes::doc("#[doc = \"__attribute__(constructor)\"]"),
attr::Attribute::Destructor(_) => attributes::doc("#[doc = \"__attribute__(destructor)\"]"),
attr::Attribute::Aligned(ref tokens) => {
let s = tokens.iter()
.map(|ref token| unsafe { str::from_utf8_unchecked(&token.raw) })
.fold(String::new(), |acc, ref s| { acc + &s });
let t = format!("#[doc = \"__attribute__(aligned{})\"]", s.as_str());

attributes::doc(&t)
}
attr::Attribute::Unexposed(ref name, ref tokens) => {
let s = tokens.iter()
.map(|ref token| unsafe { str::from_utf8_unchecked(&token.raw) })
.fold(String::new(), |acc, ref s| { acc + &s });
let t = format!("#[doc = \"__attribute__({}{})\"]", name, s);

attributes::doc(&t)
}
}
}
}
21 changes: 20 additions & 1 deletion src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,11 +426,19 @@ impl CodeGenerator for Var {
result.saw_var(&canonical_name);

let ty = self.ty().to_rust_ty(ctx);
let mut attrs = vec![];

if ctx.options().generate_comments {
for attr in self.attributes() {
attrs.push(helpers::ast_ty::attribute_to_rust(attr));
}
}

if let Some(val) = self.val() {
let const_item = aster::AstBuilder::new()
.item()
.pub_()
.with_attrs(attrs)
.const_(canonical_name)
.expr();
let item = match *val {
Expand Down Expand Up @@ -474,7 +482,6 @@ impl CodeGenerator for Var {

result.push(item);
} else {
let mut attrs = vec![];
if let Some(mangled) = self.mangled_name() {
attrs.push(attributes::link_name(mangled));
} else if canonical_name != self.name() {
Expand Down Expand Up @@ -877,6 +884,9 @@ impl CodeGenerator for CompInfo {
if let Some(comment) = item.comment() {
attributes.push(attributes::doc(comment));
}
for attr in self.attributes() {
attributes.push(helpers::ast_ty::attribute_to_rust(attr));
}
}
if self.packed() {
attributes.push(attributes::repr_list(&["C", "packed"]));
Expand Down Expand Up @@ -1091,6 +1101,9 @@ impl CodeGenerator for CompInfo {
if let Some(comment) = field.comment() {
attrs.push(attributes::doc(comment));
}
for attr in field.attributes() {
attrs.push(helpers::ast_ty::attribute_to_rust(attr));
}
}
let field_name = match field.name() {
Some(name) => ctx.rust_mangle(name).into_owned(),
Expand Down Expand Up @@ -1791,6 +1804,9 @@ impl CodeGenerator for Enum {
if let Some(comment) = item.comment() {
builder = builder.with_attr(attributes::doc(comment));
}
for attr in self.attributes() {
builder = builder.with_attr(helpers::ast_ty::attribute_to_rust(attr));
}
}

if !is_constified_enum {
Expand Down Expand Up @@ -2217,6 +2233,9 @@ impl CodeGenerator for Function {
if let Some(comment) = item.comment() {
attributes.push(attributes::doc(comment));
}
for attr in self.attributes() {
attributes.push(helpers::ast_ty::attribute_to_rust(attr));
}
}

if let Some(mangled) = mangled_name {
Expand Down
153 changes: 153 additions & 0 deletions src/ir/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! Intermediate representation for attributes.
use std::str;

use clang::Cursor;
use clang_sys::{CXCursor_UnexposedAttr, CXChildVisit_Continue};
use cexpr::token::{Token, Kind};

use super::context::BindgenContext;

/// The special attribute
#[derive(Clone, Debug)]
pub enum Attribute {
/// This attribute results in a warning if the type is used anywhere in the source file.
Deprecated(Option<String>),
/// This attribute means that variables of that type are meant to appear possibly unused.
Unused,
/// This attribute attached to a function, means that code must be emitted for the function
/// even if it appears that the function is not referenced.
Used,
/// This attribute on functions is used to inform the compiler that the function is unlikely to be executed.
Cold,
/// Many functions do not examine any values except their arguments,
/// and have no effects except the return value.
Const,
/// This attribute causes the function to be called automatically before execution enters main ().
Constructor(Option<isize>),
/// This attribute causes the function to be called automatically after main () completes or exit () is called.
Destructor(Option<isize>),
/// This attribute specifies a minimum alignment (in bytes)
Aligned(Vec<Token>),
/// An attribute whose specific kind is not exposed via this interface.
Unexposed(String, Vec<Token>)
}

impl Attribute {
/// Construct a new `Attribute`.
pub fn new(tokens: Vec<Token>) -> Self {
// https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute-Syntax
assert!(!tokens.is_empty());

let (token, args) = tokens.split_first().unwrap();

assert_eq!(token.kind, Kind::Identifier);

let name = unsafe { str::from_utf8_unchecked(&token.raw) };

debug!("__attribute__(({}({:?})))", name, args);

match name {
"deprecated" => {
let text = if args.len() == 3 &&
args[0].kind == Kind::Punctuation && args[0].raw.as_ref() == b"(" &&
args[1].kind == Kind::Literal &&
args[2].kind == Kind::Punctuation && args[2].raw.as_ref() == b")" {
str::from_utf8(args[1].raw.as_ref()).ok().map(String::from)
} else {
None
};

Attribute::Deprecated(text)
}
"unused" => Attribute::Unused,
"used" => Attribute::Used,
"cold" => Attribute::Cold,
"const" => Attribute::Const,
"constructor" => {
let priority = if args.len() == 3 &&
args[0].kind == Kind::Punctuation && args[0].raw.as_ref() == b"(" &&
args[1].kind == Kind::Literal &&
args[2].kind == Kind::Punctuation && args[2].raw.as_ref() == b")" {
str::from_utf8(args[1].raw.as_ref()).ok().and_then(|s| s.parse::<isize>().ok())
} else {
None
};

Attribute::Constructor(priority)
}
"destructor" => {
let priority = if args.len() == 3 &&
args[0].kind == Kind::Punctuation && args[0].raw.as_ref() == b"(" &&
args[1].kind == Kind::Literal &&
args[2].kind == Kind::Punctuation && args[2].raw.as_ref() == b")" {
str::from_utf8(args[1].raw.as_ref()).ok().and_then(|s| s.parse::<isize>().ok())
} else {
None
};

Attribute::Destructor(priority)
}
"aligned" => Attribute::Aligned(Vec::from(args)),
_ => Attribute::Unexposed(String::from(name), Vec::from(args)),
}
}

/// Parse a `Cursor` for `Vec<Attribute>`.
pub fn parse(cur: &Cursor, ctx: &BindgenContext) -> Vec<Self> {
let mut attributes = vec![];

if let Some(tokens) = ctx.translation_unit().cexpr_tokens(&cur) {
let mut c = 0;
let mut iter = tokens.iter();

while c >= 0 {
let tokens = iter.by_ref().take_while(|ref token| {
if token.kind == Kind::Punctuation {
c += match token.raw.as_ref() {
b"(" => 1,
b")" => -1,
b"," if c == 0 => return false,
_ => 0,
}
}

c >= 0
}).map(|token| token.clone()).collect::<Vec<Token>>();

if tokens.is_empty() {
break
} else {
attributes.push(Attribute::new(tokens));
}
}
}

attributes
}

/// Extract `Vec<Attribute>` from cursor's children.
pub fn extract(cur: &Cursor, ctx: &BindgenContext) -> Vec<Self> {
let mut attributes = vec![];

cur.visit(|cur| {
match cur.kind() {
CXCursor_UnexposedAttr => {
attributes.append(&mut Attribute::parse(&cur, ctx))
}
_ => {}
}
CXChildVisit_Continue
});

attributes
}

/// Whether this attribute whose specific kind is not exposed.
pub fn is_unexposed(&self) -> bool {
match *self {
Attribute::Unexposed(..) |
Attribute::Aligned(..) => true,
_ => false,
}
}
}
Loading

0 comments on commit ba747cd

Please sign in to comment.