//! Helpers for code generation that need struct layout use super::helpers::BlobTyBuilder; use aster::struct_field::StructFieldBuilder; use ir::comp::CompInfo; use ir::context::BindgenContext; use ir::layout::Layout; use ir::ty::Type; use std::cmp; use std::mem; use syntax::ast; /// Trace the layout of struct. pub struct StructLayoutTracker<'a, 'ctx: 'a> { ctx: &'a BindgenContext<'ctx>, comp: &'a CompInfo, latest_offset: usize, padding_count: usize, latest_field_layout: Option, max_field_align: usize, } impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { pub fn new(ctx: &'a BindgenContext<'ctx>, comp: &'a CompInfo) -> Self { StructLayoutTracker { ctx: ctx, comp: comp, latest_offset: 0, padding_count: 0, latest_field_layout: None, max_field_align: 0, } } pub fn saw_vtable(&mut self) { let ptr_size = mem::size_of::<*mut ()>(); self.latest_offset += ptr_size; self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size)); self.max_field_align = ptr_size; } pub fn saw_base(&mut self, base_ty: &Type) { self.align_to_latest_field(); if let Some(layout) = base_ty.layout(self.ctx) { self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); self.max_field_align = cmp::max(self.max_field_align, layout.align); } } pub fn saw_bitfield(&mut self, layout: Layout) { self.align_to_latest_field(); self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); self.max_field_align = cmp::max(self.max_field_align, layout.align); } pub fn saw_union(&mut self, layout: Layout) { self.align_to_latest_field(); self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); self.max_field_align = cmp::max(self.max_field_align, layout.align); } pub fn pad_field(&mut self, field_name: &str, field_ty: &Type, field_offset: Option) -> Option { field_ty.layout(self.ctx).and_then(|field_layout| { self.align_to_latest_field(); let padding_layout = if self.comp.packed() { None } else { let calculated_layout = field_ty.as_comp() .and_then(|comp| comp.calc_layout(self.ctx)) .unwrap_or(field_layout); let align = cmp::min(calculated_layout.align, mem::size_of::<*mut ()>()); let (padding_bytes, need_padding) = match field_offset { Some(offset) if offset / 8 > self.latest_offset => { (offset / 8 - self.latest_offset, true) } _ if field_layout.align != 0 => { (self.padding_bytes(field_layout), (self.latest_offset % field_layout.align) != 0) } _ => { (0, false) } }; self.latest_offset += padding_bytes; debug!("align field {} to {}/{} with {} padding bytes {:?}, calculated {:?}", field_name, self.latest_offset, field_offset.unwrap_or(0) / 8, padding_bytes, field_layout, calculated_layout); if need_padding && (padding_bytes > calculated_layout.align || field_layout.align > mem::size_of::<*mut ()>()) { Some(Layout::new(padding_bytes, align)) } else { None } }; self.latest_offset += field_ty.calc_size(self.ctx).unwrap_or(field_layout.size); self.latest_field_layout = Some(field_layout); self.max_field_align = cmp::max(self.max_field_align, field_layout.align); padding_layout.map(|layout| self.padding_field(layout)) }) } pub fn pad_struct(&mut self, layout: Layout) -> Option { if layout.size < self.latest_offset { warn!("calculate struct layout incorrect, too more {} bytes", self.latest_offset - layout.size); None } else { let padding_bytes = layout.size - self.latest_offset; let struct_align = cmp::min(layout.align, mem::size_of::<*mut ()>()); if padding_bytes > struct_align || (layout.align > mem::size_of::<*mut ()>() && padding_bytes > 0) { let padding_align = if self.comp.packed() { 1 } else { cmp::min(1 << padding_bytes.trailing_zeros(), mem::size_of::<*mut ()>()) }; Some(self.padding_field(Layout::new(padding_bytes, padding_align))) } else { None } } } pub fn align_struct(&self, layout: Layout) -> Option { if self.max_field_align < layout.align && layout.align <= mem::size_of::<*mut ()>() { let ty = BlobTyBuilder::new(Layout::new(0, layout.align)).build(); Some(StructFieldBuilder::named("__bindgen_align") .pub_() .build_ty(ty)) } else { None } } fn padding_bytes(&self, layout: Layout) -> usize { if layout.align == 0 { warn!("try to padding bytes without layout"); 0 } else if self.latest_offset % layout.align == 0 { 0 } else { layout.align - (self.latest_offset % layout.align) } } fn padding_field(&mut self, layout: Layout) -> ast::StructField { let ty = BlobTyBuilder::new(layout).build(); let padding_count = self.padding_count; self.padding_count += 1; let padding_field_name = format!("__bindgen_padding_{}", padding_count); self.max_field_align = cmp::max(self.max_field_align, layout.align); StructFieldBuilder::named(padding_field_name).pub_().build_ty(ty) } fn align_to_latest_field(&mut self) { if self.comp.packed() { // skip to align field when packed } else if let Some(layout) = self.latest_field_layout { self.latest_offset += self.padding_bytes(layout); } } }