Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract and parse unexporsed attributes #459

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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