use sigmd::model::{Function, Parameter, ParameterFlags, Type};
use super::{BuildContext, Error, Score, clang, parameter::build_parameter, sal, ty::build_type};
#[derive(Debug)]
pub struct ScoredFunction {
pub function: Function,
pub score: Score,
}
pub fn build_function(
cursor: clang::Entity<'_>,
ctx: &BuildContext,
) -> Result<ScoredFunction, Error> {
let name = match cursor.get_name() {
Some(name) => name,
None => {
return Err(Error::UndefinedProperty {
property: "name",
entity: format!("{:?}", cursor.get_kind()),
});
}
};
let return_ty = match cursor.get_result_type() {
Some(return_ty) => build_type(return_ty, ctx),
None => {
return Err(Error::UndefinedProperty {
property: "return type",
entity: format!("{:?}", cursor.get_kind()),
});
}
};
let mut parameters = Vec::new();
let mut parameter_invalid = Vec::new();
let mut sal_per_parameter = Vec::new();
if cursor.get_kind() == clang::EntityKind::Method {
parameters.push(
Parameter::builder()
.name("This")
.ty(Type::void_pointer())
.build(),
);
parameter_invalid.push(false);
sal_per_parameter.push(Vec::new());
}
let mut function_annotations = Vec::new();
for child in cursor.get_children() {
match child.get_kind() {
clang::EntityKind::ParmDecl => {
let bp = build_parameter(child, ctx)?;
sal_per_parameter.push(bp.annotations);
parameter_invalid.push(bp.is_invalid);
parameters.push(bp.parameter);
}
clang::EntityKind::AnnotateAttr => {
if let Some(name) = child.get_name() {
function_annotations.push(name);
}
}
_ => {}
}
}
let annotations_per_parameter = sal_per_parameter
.iter()
.map(|raw| sal::decode(raw))
.collect::<Vec<_>>();
let buffers = sal::analyze(¶meters, &annotations_per_parameter, ctx);
let has_override = function_annotations
.iter()
.any(|annotation| annotation.starts_with("__OVERRIDE"));
let is_invalid = cursor.is_invalid_declaration();
let function = Function::builder()
.name(name)
.parameters(parameters)
.buffers(buffers)
.return_ty(return_ty)
.build();
let score = function_score(&function, ¶meter_invalid, is_invalid, has_override);
Ok(ScoredFunction { function, score })
}
fn function_score(
function: &Function,
parameter_invalid: &[bool],
function_invalid: bool,
has_override: bool,
) -> Score {
let mut score = 0;
for (i, param) in function.parameters.iter().enumerate() {
score += 1;
if param.flags.contains(ParameterFlags::HAS_IN_ATTRIBUTE) {
score += 10;
}
if param.flags.contains(ParameterFlags::HAS_OUT_ATTRIBUTE) {
score += 10;
}
let invalid = parameter_invalid.get(i).copied().unwrap_or(false);
if !invalid {
score += 100;
}
}
if !function_invalid {
score += 100_000;
}
if has_override {
score += 1_000_000;
}
Score(score)
}
#[cfg(test)]
mod tests {
use sigmd::model::{Function, Parameter, ParameterFlags, Type, TypeKind};
use super::*;
#[test]
fn function_score_weights_match_spec() {
let func = Function::builder()
.name("X")
.parameters(vec![
Parameter::builder()
.name("p1")
.flags(ParameterFlags::HAS_IN_ATTRIBUTE)
.ty(Type::builder().name("int").kind(TypeKind::I32).build())
.build(),
Parameter::builder()
.name("p2")
.flags(ParameterFlags::HAS_OUT_ATTRIBUTE)
.ty(Type::builder()
.indirections(1)
.name("int")
.kind(TypeKind::I32)
.build())
.build(),
])
.return_ty(Type::builder().name("void").kind(TypeKind::Void).build())
.build();
let s = function_score(&func, &[false, false], false, false);
assert_eq!(s.0, 100_222);
let s = function_score(&func, &[false, false], false, true);
assert_eq!(s.0, 1_100_222);
let s = function_score(&func, &[false, false], true, false);
assert_eq!(s.0, 222);
}
}