use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use std::collections::HashSet;
use std::collections::HashMap;
use ir::context::{BindgenContext, ItemId};
use ir::traversal::EdgeKind;
use ir::ty::TypeKind;
#[derive(Debug, Clone)]
pub struct HasVtableAnalysis<'ctx, 'gen>
where 'gen: 'ctx
{
ctx: &'ctx BindgenContext<'gen>,
have_vtable: HashSet<ItemId>,
dependencies: HashMap<ItemId, Vec<ItemId>>,
}
impl<'ctx, 'gen> HasVtableAnalysis<'ctx, 'gen> {
fn consider_edge(kind: EdgeKind) -> bool {
match kind {
EdgeKind::TypeReference |
EdgeKind::BaseMember |
EdgeKind::TemplateDeclaration => true,
_ => false,
}
}
fn insert(&mut self, id: ItemId) -> ConstrainResult {
let was_not_already_in_set = self.have_vtable.insert(id);
assert!(
was_not_already_in_set,
"We shouldn't try and insert {:?} twice because if it was \
already in the set, `constrain` should have exited early.",
id
);
ConstrainResult::Changed
}
}
impl<'ctx, 'gen> MonotoneFramework for HasVtableAnalysis<'ctx, 'gen> {
type Node = ItemId;
type Extra = &'ctx BindgenContext<'gen>;
type Output = HashSet<ItemId>;
fn new(ctx: &'ctx BindgenContext<'gen>) -> HasVtableAnalysis<'ctx, 'gen> {
let have_vtable = HashSet::new();
let dependencies = generate_dependencies(ctx, Self::consider_edge);
HasVtableAnalysis {
ctx,
have_vtable,
dependencies,
}
}
fn initial_worklist(&self) -> Vec<ItemId> {
self.ctx.whitelisted_items().iter().cloned().collect()
}
fn constrain(&mut self, id: ItemId) -> ConstrainResult {
if self.have_vtable.contains(&id) {
return ConstrainResult::Same;
}
let item = self.ctx.resolve_item(id);
let ty = match item.as_type() {
None => return ConstrainResult::Same,
Some(ty) => ty
};
match *ty.kind() {
TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(t) |
TypeKind::ResolvedTypeRef(t) |
TypeKind::Reference(t) => {
if self.have_vtable.contains(&t) {
self.insert(id)
} else {
ConstrainResult::Same
}
},
TypeKind::Comp(ref info) => {
if info.has_own_virtual_method() {
return self.insert(id);
}
let bases_has_vtable = info.base_members().iter().any(|base| {
self.have_vtable.contains(&base.ty)
});
if bases_has_vtable {
self.insert(id)
} else {
ConstrainResult::Same
}
},
TypeKind::TemplateInstantiation(ref inst) => {
if self.have_vtable.contains(&inst.template_definition()) {
self.insert(id)
} else {
ConstrainResult::Same
}
},
_ => ConstrainResult::Same,
}
}
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
where F: FnMut(ItemId),
{
if let Some(edges) = self.dependencies.get(&id) {
for item in edges {
trace!("enqueue {:?} into worklist", item);
f(*item);
}
}
}
}
impl<'ctx, 'gen> From<HasVtableAnalysis<'ctx, 'gen>> for HashSet<ItemId> {
fn from(analysis: HasVtableAnalysis<'ctx, 'gen>) -> Self {
analysis.have_vtable
}
}
pub trait HasVtable {
fn has_vtable(&self, ctx: &BindgenContext) -> bool;
}