use crate::labels::Cap;
use crate::ssa::ir::{SsaBody, Terminator};
use crate::summary::ssa_summary::PathFactReturnEntry;
use crate::symbol::FuncKey;
use crate::taint::domain::{TaintOrigin, VarTaint};
use petgraph::graph::NodeIndex;
use smallvec::SmallVec;
use std::collections::HashMap;
pub(super) const MAX_INLINE_BLOCKS: usize = 500;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ArgTaintSig(pub(super) SmallVec<[(usize, u16); 4]>);
#[derive(Clone, Debug)]
pub(crate) struct InlineResult {
pub(super) return_taint: Option<VarTaint>,
pub(super) return_path_fact: crate::abstract_interp::PathFact,
#[allow(dead_code)]
pub(super) return_path_facts: SmallVec<[PathFactReturnEntry; 2]>,
}
#[derive(Clone, Debug)]
pub(crate) struct CachedInlineShape(pub(super) Option<ReturnShape>);
#[derive(Clone, Debug)]
pub(crate) struct ReturnShape {
pub(super) caps: Cap,
pub(super) internal_origins: SmallVec<[TaintOrigin; 2]>,
pub(super) param_provenance: u64,
pub(super) receiver_provenance: bool,
pub(super) uses_summary: bool,
pub(super) return_path_fact: crate::abstract_interp::PathFact,
pub(super) return_path_facts: SmallVec<[PathFactReturnEntry; 2]>,
}
impl CachedInlineShape {
fn return_caps_bits(&self) -> u16 {
self.0.as_ref().map(|s| s.caps.bits()).unwrap_or(0)
}
}
pub(crate) type InlineCache = HashMap<(FuncKey, ArgTaintSig), CachedInlineShape>;
#[allow(dead_code)]
pub(crate) fn inline_cache_clear_epoch(cache: &mut InlineCache) {
cache.clear();
}
#[allow(dead_code)]
pub(crate) fn inline_cache_fingerprint(
cache: &InlineCache,
) -> HashMap<(FuncKey, ArgTaintSig), u16> {
cache
.iter()
.map(|(k, v)| (k.clone(), v.return_caps_bits()))
.collect()
}
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CrossFileNodeMeta {
pub info: crate::cfg::NodeInfo,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct CalleeSsaBody {
pub ssa: SsaBody,
pub opt: crate::ssa::OptimizeResult,
pub param_count: usize,
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub node_meta: std::collections::HashMap<u32, CrossFileNodeMeta>,
#[serde(skip)]
pub body_graph: Option<crate::cfg::Cfg>,
}
pub fn populate_node_meta(body: &mut CalleeSsaBody, cfg: &crate::cfg::Cfg) -> bool {
let mut referenced: Vec<NodeIndex> = Vec::new();
for block in &body.ssa.blocks {
for inst in block.phis.iter().chain(block.body.iter()) {
referenced.push(inst.cfg_node);
}
if let Terminator::Branch { cond, .. } = &block.terminator {
referenced.push(*cond);
}
}
for node in referenced {
let idx = node.index() as u32;
if body.node_meta.contains_key(&idx) {
continue;
}
if node.index() >= cfg.node_count() {
return false;
}
let info = cfg[node].clone();
body.node_meta.insert(idx, CrossFileNodeMeta { info });
}
true
}
pub fn rebuild_body_graph(body: &mut CalleeSsaBody) -> bool {
if body.body_graph.is_some() {
return false;
}
if body.node_meta.is_empty() {
return false;
}
let mut max_idx: u32 = 0;
for block in &body.ssa.blocks {
for inst in block.phis.iter().chain(block.body.iter()) {
let idx = inst.cfg_node.index() as u32;
if idx > max_idx {
max_idx = idx;
}
}
if let Terminator::Branch { cond, .. } = &block.terminator {
let idx = cond.index() as u32;
if idx > max_idx {
max_idx = idx;
}
}
}
for &k in body.node_meta.keys() {
if k > max_idx {
max_idx = k;
}
}
use petgraph::graph::Graph;
let mut graph: crate::cfg::Cfg = Graph::new();
for i in 0..=max_idx {
let info = body
.node_meta
.get(&i)
.map(|m| m.info.clone())
.unwrap_or_default();
graph.add_node(info);
}
body.body_graph = Some(graph);
true
}