From 61743aa190772a1c75acb75d274215274d0e6873 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 22 Aug 2017 18:53:55 -0700 Subject: Use `quote` instead of `syntex` for Rust code generation The `syntex` crate is unmaintained. It is slow to build, and additionally it requires that we pre-process `src/codegen/mod.rs` before we build the `bindgen` crate. The `quote` crate provides similar quasi-quoting functionality, is maintained, and builds faster. It doesn't have a typed API or builders, however; it only deals with tokens. Before this commit: ``` $ cargo clean; cargo build Finished dev [unoptimized + debuginfo] target(s) in 98.75 secs ``` After this commit: ``` $ cargo clean; cargo build Finished dev [unoptimized + debuginfo] target(s) in 46.26 secs ``` Build time is cut in half! But what about run time? Before this commit: ``` Generated Stylo bindings in: Duration { secs: 3, nanos: 521105668 } ``` After this commit: ``` Generated Stylo bindings in: Duration { secs: 3, nanos: 548797242 } ``` So it appears to be about 20ms slower at generating Stylo bindings, but I suspect this is well within the noise. Finally, this also lets us remove that nasty `mem::transmute` inside `bindgen::ir::BindgenContext::gen` that was used for the old `syntex` context. Now `BindgenContext` doesn't have a lifetime parameter either. This should make it easier to revisit doing our analyses in parallel with `rayon`, since that context was one of the things that made it hard for `BindgenContext` to implement `Sync`. Fixes #925 --- src/codegen/helpers.rs | 211 ++++++++++++++++++++++++------------------------- 1 file changed, 104 insertions(+), 107 deletions(-) (limited to 'src/codegen/helpers.rs') diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs index ed165aa9..822922cd 100644 --- a/src/codegen/helpers.rs +++ b/src/codegen/helpers.rs @@ -1,66 +1,62 @@ //! Helpers for code generation that don't need macro expansion. -use aster; use ir::layout::Layout; -use syntax::ast; -use syntax::ptr::P; - +use quote; pub mod attributes { - use aster; - use syntax::ast; - - pub fn allow(which_ones: &[&str]) -> ast::Attribute { - aster::AstBuilder::new() - .attr() - .list("allow") - .words(which_ones) - .build() - } + use quote; - pub fn repr(which: &str) -> ast::Attribute { - aster::AstBuilder::new() - .attr() - .list("repr") - .words(&[which]) - .build() + pub fn repr(which: &str) -> quote::Tokens { + let which = quote::Ident::new(which); + quote! { + #[repr( #which )] + } } - pub fn repr_list(which_ones: &[&str]) -> ast::Attribute { - aster::AstBuilder::new() - .attr() - .list("repr") - .words(which_ones) - .build() + pub fn repr_list(which_ones: &[&str]) -> quote::Tokens { + let which_ones = which_ones.iter().cloned().map(quote::Ident::new); + quote! { + #[repr( #( #which_ones ),* )] + } } - pub fn derives(which_ones: &[&str]) -> ast::Attribute { - aster::AstBuilder::new() - .attr() - .list("derive") - .words(which_ones) - .build() + pub fn derives(which_ones: &[&str]) -> quote::Tokens { + let which_ones = which_ones.iter().cloned().map(quote::Ident::new); + quote! { + #[derive( #( #which_ones ),* )] + } } - pub fn inline() -> ast::Attribute { - aster::AstBuilder::new().attr().word("inline") + pub fn inline() -> quote::Tokens { + quote! { + #[inline] + } } - pub fn doc(comment: String) -> ast::Attribute { - aster::AstBuilder::new().attr().doc(&*comment) + pub fn doc(comment: String) -> quote::Tokens { + // Doc comments are already preprocessed into nice `///` formats by the + // time they get here. Just make sure that we have newlines around it so + // that nothing else gets wrapped into the comment. + let mut tokens = quote! {}; + tokens.append("\n"); + tokens.append(comment); + tokens.append("\n"); + tokens } - pub fn link_name(name: &str) -> ast::Attribute { - aster::AstBuilder::new() - .attr() - .name_value("link_name") - .str(name) + pub fn link_name(name: &str) -> quote::Tokens { + // This is ridiculous, but rustfmt doesn't seem to be formatting + // `link_name` attributes very well, so we jump through these formatting + // hoops to make it work. + let mut tokens = quote! {}; + tokens.append(format!(" #[link_name = \"{}\"]", name)); + tokens } } /// Generates a proper type for a field or type with a given `Layout`, that is, /// a type with the correct size and alignment restrictions. -pub fn blob(layout: Layout) -> P { +pub fn blob(layout: Layout) -> quote::Tokens { let opaque = layout.opaque(); // FIXME(emilio, #412): We fall back to byte alignment, but there are @@ -75,39 +71,46 @@ pub fn blob(layout: Layout) -> P { } }; + let ty_name = quote::Ident::new(ty_name); + let data_len = opaque.array_size().unwrap_or(layout.size); - let inner_ty = aster::AstBuilder::new().ty().path().id(ty_name).build(); if data_len == 1 { - inner_ty + quote! { + #ty_name + } } else { - aster::ty::TyBuilder::new().array(data_len).build(inner_ty) + quote! { + [ #ty_name ; #data_len ] + } } } pub mod ast_ty { - use aster; use ir::context::BindgenContext; use ir::function::FunctionSig; use ir::ty::FloatKind; - use syntax::ast; - use syntax::ptr::P; + use quote; - pub fn raw_type(ctx: &BindgenContext, name: &str) -> P { - let ident = ctx.rust_ident_raw(&name); + pub fn raw_type(ctx: &BindgenContext, name: &str) -> quote::Tokens { + let ident = ctx.rust_ident_raw(name); match ctx.options().ctypes_prefix { Some(ref prefix) => { - let prefix = ctx.rust_ident_raw(prefix); - quote_ty!(ctx.ext_cx(), $prefix::$ident) + let prefix = ctx.rust_ident_raw(prefix.as_str()); + quote! { + #prefix::#ident + } } - None => quote_ty!(ctx.ext_cx(), ::std::os::raw::$ident), + None => quote! { + ::std::os::raw::#ident + }, } } pub fn float_kind_rust_type( ctx: &BindgenContext, fk: FloatKind, - ) -> P { + ) -> quote::Tokens { // TODO: we probably should just take the type layout into // account? // @@ -116,64 +119,50 @@ pub mod ast_ty { // FIXME: `c_longdouble` doesn't seem to be defined in some // systems, so we use `c_double` directly. match (fk, ctx.options().convert_floats) { - (FloatKind::Float, true) => aster::ty::TyBuilder::new().f32(), + (FloatKind::Float, true) => quote! { f32 }, (FloatKind::Double, true) | - (FloatKind::LongDouble, true) => aster::ty::TyBuilder::new().f64(), + (FloatKind::LongDouble, true) => quote! { f64 }, (FloatKind::Float, false) => raw_type(ctx, "c_float"), (FloatKind::Double, false) | (FloatKind::LongDouble, false) => raw_type(ctx, "c_double"), - (FloatKind::Float128, _) => { - aster::ty::TyBuilder::new().array(16).u8() - } + (FloatKind::Float128, _) => quote! { [u8; 16] }, } } - pub fn int_expr(val: i64) -> P { - use std::i64; - let expr = aster::AstBuilder::new().expr(); - - // This is not representable as an i64 if it's negative, so we - // special-case it. - // - // Fix in aster incoming. - if val == i64::MIN { - expr.neg().uint(1u64 << 63) - } else { - expr.int(val) - } + pub fn int_expr(val: i64) -> quote::Tokens { + // Don't use quote! { #val } because that adds the type suffix. + let mut tokens = quote! {}; + tokens.append(val.to_string()); + tokens } - pub fn bool_expr(val: bool) -> P { - aster::AstBuilder::new().expr().bool(val) + pub fn uint_expr(val: u64) -> quote::Tokens { + // Don't use quote! { #val } because that adds the type suffix. + let mut tokens = quote! {}; + tokens.append(val.to_string()); + tokens } - pub fn byte_array_expr(bytes: &[u8]) -> P { - let mut vec = Vec::with_capacity(bytes.len() + 1); - for byte in bytes { - vec.push(int_expr(*byte as i64)); + pub fn byte_array_expr(bytes: &[u8]) -> quote::Tokens { + let mut bytes: Vec<_> = bytes.iter().cloned().collect(); + bytes.push(0); + quote! { + #bytes } - vec.push(int_expr(0)); - - let kind = ast::ExprKind::Array(vec); - - aster::AstBuilder::new().expr().build_expr_kind(kind) } - pub fn cstr_expr(mut string: String) -> P { + pub fn cstr_expr(mut string: String) -> quote::Tokens { string.push('\0'); - aster::AstBuilder::new().expr().build_lit( - aster::AstBuilder::new() - .lit() - .byte_str(string), - ) + let b = quote::ByteStr(&string); + quote! { + #b + } } pub fn float_expr( ctx: &BindgenContext, f: f64, - ) -> Result, ()> { - use aster::symbol::ToSymbol; - + ) -> Result { if f.is_finite() { let mut string = f.to_string(); @@ -182,21 +171,28 @@ pub mod ast_ty { string.push('.'); } - let kind = - ast::LitKind::FloatUnsuffixed(string.as_str().to_symbol()); - return Ok(aster::AstBuilder::new().expr().lit().build_lit(kind)); + let mut tokens = quote! {}; + tokens.append(string); + return Ok(tokens); } let prefix = ctx.trait_prefix(); + if f.is_nan() { - return Ok(quote_expr!(ctx.ext_cx(), ::$prefix::f64::NAN)); + return Ok(quote! { + ::#prefix::f64::NAN + }); } if f.is_infinite() { return Ok(if f.is_sign_positive() { - quote_expr!(ctx.ext_cx(), ::$prefix::f64::INFINITY) + quote! { + ::#prefix::f64::INFINITY + } } else { - quote_expr!(ctx.ext_cx(), ::$prefix::f64::NEG_INFINITY) + quote! { + ::#prefix::f64::NEG_INFINITY + } }); } @@ -207,23 +203,24 @@ pub mod ast_ty { pub fn arguments_from_signature( signature: &FunctionSig, ctx: &BindgenContext, - ) -> Vec> { - // TODO: We need to keep in sync the argument names, so we should unify - // this with the other loop that decides them. + ) -> Vec { let mut unnamed_arguments = 0; signature .argument_types() .iter() .map(|&(ref name, _ty)| { - let arg_name = match *name { - Some(ref name) => ctx.rust_mangle(name).into_owned(), + match *name { + Some(ref name) => { + let name = ctx.rust_ident(name); + quote! { #name } + } None => { unnamed_arguments += 1; - format!("arg{}", unnamed_arguments) + let name = ctx.rust_ident(format!("arg{}", unnamed_arguments)); + quote! { #name } } - }; - aster::expr::ExprBuilder::new().id(arg_name) + } }) - .collect::>() + .collect() } } -- cgit v1.2.3