#![cfg_attr(feature = "rustc", feature(rustc_private))]
#![warn(missing_docs)]
#![allow(deprecated)]
#[cfg(feature = "rustc")]
pub(crate) mod rustc {
extern crate rustc_driver;
pub extern crate rustc_hir as hir;
pub extern crate rustc_index as index;
pub extern crate rustc_middle as middle;
pub extern crate rustc_span as span;
}
#[cfg(feature = "rustc")]
extern crate rustc_macros;
#[cfg(feature = "rustc")]
extern crate rustc_serialize;
#[cfg(feature = "rustc")]
extern crate rustc_span;
extern crate strum;
pub use flowistry_pdg::*;
pub mod dot;
pub mod ser;
mod tiny_bitset;
pub mod traverse;
pub mod utils;
use allocative::{ident_key, Allocative};
use internment::Intern;
use itertools::Itertools;
use rustc_portable::DefId;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use std::{fmt, hash::Hash, path::PathBuf};
use utils::write_sep;
use utils::serde_map_via_vec;
pub use crate::tiny_bitset::pretty as tiny_bitset_pretty;
pub use crate::tiny_bitset::TinyBitSet;
use flowistry_pdg::rustc_portable::LocalDefId;
use petgraph::graph::{EdgeIndex, EdgeReference, NodeIndex};
use petgraph::prelude::EdgeRef;
use petgraph::visit::IntoNodeIdentifiers;
pub use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
pub type Endpoint = DefId;
pub type TypeId = DefId;
pub type Function = Identifier;
pub const FLOW_GRAPH_OUT_NAME: &str = "flow-graph.o";
pub const STAT_FILE_EXT: &str = "stat.json";
#[allow(dead_code)]
mod ser_localdefid_map {
use serde::{Deserialize, Serialize};
use flowistry_pdg::rustc_proxies;
#[derive(Serialize, Deserialize)]
struct Helper(#[serde(with = "rustc_proxies::LocalDefId")] super::LocalDefId);
pub fn serialize<S: serde::Serializer, V: serde::Serialize>(
map: &super::HashMap<super::LocalDefId, V>,
serializer: S,
) -> Result<S::Ok, S::Error> {
map.iter()
.map(|(k, v)| (Helper(*k), v))
.collect::<Vec<_>>()
.serialize(serializer)
}
pub fn deserialize<'de, D: serde::Deserializer<'de>, V: serde::Deserialize<'de>>(
deserializer: D,
) -> Result<super::HashMap<super::LocalDefId, V>, D::Error> {
Ok(Vec::deserialize(deserializer)?
.into_iter()
.map(|(Helper(k), v)| (k, v))
.collect())
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Serialize, Deserialize, Allocative)]
pub struct MarkerAnnotation {
pub marker: Identifier,
pub on_return: bool,
pub on_argument: TinyBitSet,
}
impl MarkerAnnotation {
pub fn on_argument(&self, arg: u16) -> bool {
self.on_argument.contains(arg as u32).unwrap_or(false)
}
pub fn on_return(&self) -> bool {
self.on_return
}
pub fn on_self(&self) -> bool {
self.on_argument.is_empty() && !self.on_return
}
}
#[cfg(feature = "rustc")]
mod ser_defid_map {
use serde::{Deserialize, Serialize};
use flowistry_pdg::rustc_proxies;
#[derive(Serialize, Deserialize)]
struct Helper(#[serde(with = "rustc_proxies::DefId")] super::DefId);
pub fn serialize<S: serde::Serializer, V: serde::Serialize>(
map: &super::HashMap<super::DefId, V>,
serializer: S,
) -> Result<S::Ok, S::Error> {
map.iter()
.map(|(k, v)| (Helper(*k), v))
.collect::<Vec<_>>()
.serialize(serializer)
}
pub fn deserialize<'de, D: serde::Deserializer<'de>, V: serde::Deserialize<'de>>(
deserializer: D,
) -> Result<super::HashMap<super::DefId, V>, D::Error> {
Ok(Vec::deserialize(deserializer)?
.into_iter()
.map(|(Helper(k), v)| (k, v))
.collect())
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Allocative)]
pub struct DefInfo {
pub name: Identifier,
pub path: Box<[Identifier]>,
pub kind: DefKind,
#[allocative(visit = allocative_visit_simple_sized)]
pub src_info: Span,
pub markers: Box<[MarkerAnnotation]>,
}
pub struct DisplayPath<'a>(&'a [Identifier]);
impl Display for DisplayPath<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write_sep(f, "::", self.0, Display::fmt)
}
}
impl<'a> From<&'a [Identifier]> for DisplayPath<'a> {
fn from(value: &'a [Identifier]) -> Self {
Self(value)
}
}
impl<'a> From<&'a Box<[Identifier]>> for DisplayPath<'a> {
fn from(value: &'a Box<[Identifier]>) -> Self {
value.as_ref().into()
}
}
#[derive(
Clone,
Copy,
PartialEq,
Eq,
Serialize,
Deserialize,
Debug,
strum::EnumIs,
strum::AsRefStr,
Allocative,
)]
pub enum DefKind {
Fn,
Generator,
Closure,
Type,
#[deprecated = "Tracking constructors is a workaround and should not be relied upon."]
Ctor,
}
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Debug, Hash, PartialOrd, Ord)]
pub struct SourceFile(Intern<SourceFileInfo>);
impl Allocative for SourceFile {
fn visit<'a, 'b: 'a>(&self, visitor: &'a mut allocative::Visitor<'b>) {
let mut visitor = visitor.enter_self_sized::<Self>();
allocative_visit_intern_t(self.0, &mut visitor);
visitor.exit();
}
}
impl std::ops::Deref for SourceFile {
type Target = SourceFileInfo;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(
Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Allocative,
)]
pub struct SourceFileInfo {
pub file_path: String,
pub abs_file_path: PathBuf,
}
impl SourceFileInfo {
pub fn intern(self) -> SourceFile {
SourceFile(Intern::new(self))
}
}
#[derive(
Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Debug, PartialOrd, Ord, Hash, Allocative,
)]
pub struct SpanCoord {
pub line: u32,
pub col: u32,
}
#[derive(
Clone, PartialEq, Eq, Serialize, Deserialize, Debug, PartialOrd, Ord, Hash, Allocative,
)]
pub struct Span {
pub source_file: SourceFile,
pub start: SpanCoord,
pub end: SpanCoord,
}
impl Span {
pub fn contains(&self, other: &Self) -> bool {
self.source_file == other.source_file && self.start <= other.start && self.end >= other.end
}
pub fn line_len(&self) -> u32 {
self.end.line - self.start.line + 1
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Allocative)]
pub struct FunctionCallInfo {
pub is_inlined: bool,
#[cfg_attr(feature = "rustc", serde(with = "rustc_proxies::DefId"))]
#[allocative(visit = allocative_visit_simple_sized)]
pub id: DefId,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, strum::EnumIs, Allocative)]
pub enum InstructionKind {
Statement,
FunctionCall(FunctionCallInfo),
Terminator,
Start,
Return,
}
impl InstructionKind {
pub fn as_function_call(self) -> Option<FunctionCallInfo> {
match self {
InstructionKind::FunctionCall(d) => Some(d),
_ => None,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Allocative)]
pub struct InstructionInfo {
pub kind: InstructionKind,
pub span: Span,
pub description: Identifier,
}
pub type TypeInfoMap = HashMap<TypeId, TypeDescription>;
pub type ControllerMap = HashMap<Endpoint, SPDG>;
#[doc(hidden)]
#[derive(Serialize, Deserialize, Debug, Allocative)]
pub enum FunctionHandling {
PDG,
Elided,
}
#[doc(hidden)]
pub type AnalyzedSpans = HashMap<DefId, (Span, FunctionHandling)>;
#[derive(Serialize, Deserialize, Debug, Allocative)]
pub struct ProgramDescription {
#[cfg_attr(feature = "rustc", serde(with = "ser_defid_map"))]
#[cfg_attr(not(feature = "rustc"), serde(with = "serde_map_via_vec"))]
#[allocative(visit = allocative_visit_map_coerce_key)]
pub controllers: ControllerMap,
#[cfg_attr(not(feature = "rustc"), serde(with = "serde_map_via_vec"))]
#[cfg_attr(feature = "rustc", serde(with = "ser_defid_map"))]
#[allocative(visit = allocative_visit_map_coerce_key)]
pub type_info: TypeInfoMap,
#[serde(with = "serde_map_via_vec")]
pub instruction_info: HashMap<GlobalLocation, InstructionInfo>,
#[cfg_attr(not(feature = "rustc"), serde(with = "serde_map_via_vec"))]
#[cfg_attr(feature = "rustc", serde(with = "ser_defid_map"))]
#[allocative(visit = allocative_visit_map_coerce_key)]
pub def_info: HashMap<DefId, DefInfo>,
#[doc(hidden)]
#[cfg_attr(not(feature = "rustc"), serde(with = "serde_map_via_vec"))]
#[cfg_attr(feature = "rustc", serde(with = "ser_defid_map"))]
#[allocative(visit = allocative_visit_map_coerce_key)]
pub analyzed_spans: AnalyzedSpans,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AnalyzerStats {
pub marker_annotation_count: u32,
pub self_time: Duration,
pub dump_time: Duration,
pub tycheck_time: Duration,
pub dep_time: Duration,
pub rustc_time: Duration,
pub serialization_time: Duration,
pub pdg_functions: u32,
pub pdg_locs: u32,
pub seen_functions: u32,
pub seen_locs: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, Allocative)]
pub struct TypeDescription {
pub rendering: String,
#[cfg_attr(feature = "rustc", serde(with = "ser_defid_seq"))]
#[allocative(visit = allocative_visit_box_slice_simple_t)]
pub otypes: Box<[TypeId]>,
pub markers: Vec<Identifier>,
}
#[allow(clippy::borrowed_box)]
fn allocative_visit_box_slice_simple_t<T>(item: &Box<[T]>, visitor: &mut allocative::Visitor<'_>) {
let coerced: &Box<[SimpleSizedAllocativeWrapper<T>]> =
unsafe { std::mem::transmute::<&Box<[T]>, &Box<[SimpleSizedAllocativeWrapper<T>]>>(item) };
coerced.visit(visitor);
}
#[cfg(feature = "rustc")]
mod ser_defid_seq {
use flowistry_pdg::rustc_proxies;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Serialize, Deserialize)]
#[repr(transparent)]
struct DefIdWrap(#[serde(with = "rustc_proxies::DefId")] crate::DefId);
pub fn serialize<S: Serializer>(v: &[crate::DefId], serializer: S) -> Result<S::Ok, S::Error> {
unsafe {
<[DefIdWrap]>::serialize(
std::mem::transmute::<&[rustc_span::def_id::DefId], &[DefIdWrap]>(v),
serializer,
)
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Box<[crate::DefId]>, D::Error> {
unsafe {
Ok(std::mem::transmute::<
std::boxed::Box<[DefIdWrap]>,
std::boxed::Box<[rustc_span::def_id::DefId]>,
>(Box::<[DefIdWrap]>::deserialize(
deserializer,
)?))
}
}
}
impl ProgramDescription {
pub fn all_nodes(&self) -> HashSet<GlobalNode> {
self.controllers
.iter()
.flat_map(|(name, c)| {
c.all_sources()
.map(|ds| GlobalNode::from_local_node(*name, ds))
})
.collect()
}
pub fn all_call_sites(&self) -> HashSet<CallString> {
self.controllers
.values()
.flat_map(|v| {
v.graph
.edge_weights()
.map(|e| e.at)
.chain(v.graph.node_weights().map(|n| n.at))
})
.collect()
}
}
#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize, Copy)]
pub struct Identifier(Intern<String>);
impl Allocative for Identifier {
fn visit<'a, 'b: 'a>(&self, visitor: &'a mut allocative::Visitor<'b>) {
let mut visitor = visitor.enter_self_sized::<Self>();
allocative_visit_intern_t(self.0, &mut visitor);
visitor.exit();
}
}
impl<'a> From<&'a str> for Identifier {
fn from(value: &'a str) -> Self {
Identifier::new_intern(value)
}
}
#[cfg(feature = "rustc")]
impl<S: rustc_serialize::Encoder> rustc_serialize::Encodable<S> for Identifier {
fn encode(&self, s: &mut S) {
s.emit_str(self.as_str());
}
}
#[cfg(feature = "rustc")]
impl<D: rustc_serialize::Decoder> rustc_serialize::Decodable<D> for Identifier {
fn decode(d: &mut D) -> Self {
Identifier::new_intern(d.read_str())
}
}
impl Identifier {
#[cfg(feature = "rustc")]
pub fn new(s: rustc::span::Symbol) -> Self {
Self::new_intern(s.as_str())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn new_intern(s: &str) -> Self {
Identifier(Intern::from_ref(s))
}
}
impl fmt::Debug for Identifier {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self.0.as_ref(), f)
}
}
impl Display for Identifier {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
self.0.as_ref().fmt(f)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ShortHash(u64);
impl ShortHash {
pub fn new<T: Hash>(t: T) -> Self {
Self(hash_pls(t) % 0x1_000_000)
}
}
impl fmt::Display for ShortHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:06x}", self.0)
}
}
#[test]
fn short_hash_always_six_digits() {
assert_eq!(format!("{}", ShortHash(0x0)).len(), 6);
assert_eq!(format!("{}", ShortHash(0x57110)).len(), 6);
}
pub fn hash_pls<T: Hash>(t: T) -> u64 {
use std::hash::Hasher;
let mut hasher = std::collections::hash_map::DefaultHasher::default();
t.hash(&mut hasher);
hasher.finish()
}
pub struct GlobalNodeIter<I: IntoIterGlobalNodes> {
controller_id: Endpoint,
iter: I::Iter,
}
impl<I: IntoIterGlobalNodes> Iterator for GlobalNodeIter<I> {
type Item = GlobalNode;
fn next(&mut self) -> Option<Self::Item> {
Some(GlobalNode {
controller_id: self.controller_id,
node: self.iter.next()?,
})
}
}
pub trait IntoIterGlobalNodes: Sized + Copy {
type Iter: Iterator<Item = Node>;
fn iter_nodes(self) -> Self::Iter;
fn controller_id(self) -> Endpoint;
fn iter_global_nodes(self) -> GlobalNodeIter<Self> {
GlobalNodeIter {
controller_id: self.controller_id(),
iter: self.iter_nodes(),
}
}
fn extended(self, other: impl IntoIterGlobalNodes) -> Option<NodeCluster> {
if self.controller_id() != other.controller_id() {
return None;
}
Some(NodeCluster::new(
self.controller_id(),
self.iter_nodes().chain(other.iter_nodes()).peekable(),
))
}
fn to_local_cluster(self) -> NodeCluster {
NodeCluster::new(self.controller_id(), self.iter_nodes())
}
fn one(&self) -> GlobalNode {
self.iter_global_nodes()
.next()
.expect("Invariant broken: no nodes")
}
}
impl<T: IntoIterGlobalNodes> IntoIterGlobalNodes for &T {
type Iter = T::Iter;
fn iter_nodes(self) -> Self::Iter {
(*self).iter_nodes()
}
fn controller_id(self) -> Endpoint {
(*self).controller_id()
}
}
pub type Node = NodeIndex;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct GlobalNode {
node: Node,
controller_id: Endpoint,
}
impl GlobalNode {
pub fn unsafe_new(ctrl_id: Endpoint, index: usize) -> Self {
GlobalNode {
controller_id: ctrl_id,
node: crate::Node::new(index),
}
}
pub fn from_local_node(ctrl_id: Endpoint, node: Node) -> Self {
GlobalNode {
controller_id: ctrl_id,
node,
}
}
pub fn local_node(self) -> Node {
self.node
}
pub fn controller_id(self) -> Endpoint {
self.controller_id
}
}
impl IntoIterGlobalNodes for GlobalNode {
type Iter = std::iter::Once<Node>;
fn iter_nodes(self) -> Self::Iter {
std::iter::once(self.local_node())
}
fn controller_id(self) -> Endpoint {
self.controller_id
}
}
pub mod node_cluster {
use std::ops::Range;
use crate::{Endpoint, GlobalNode, IntoIterGlobalNodes, Node};
#[derive(Debug, Hash, Clone)]
pub struct NodeCluster {
controller_id: Endpoint,
nodes: Box<[Node]>,
}
pub struct IntoIter {
inner: NodeCluster,
idx: Range<usize>,
}
impl Iterator for IntoIter {
type Item = GlobalNode;
fn next(&mut self) -> Option<Self::Item> {
let idx = self.idx.next()?;
Some(GlobalNode::from_local_node(
self.inner.controller_id,
self.inner.nodes[idx],
))
}
}
pub struct Iter<'a> {
inner: std::slice::Iter<'a, Node>,
}
impl Iterator for Iter<'_> {
type Item = Node;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().copied()
}
}
impl<'a> IntoIterGlobalNodes for &'a NodeCluster {
type Iter = Iter<'a>;
fn iter_nodes(self) -> Self::Iter {
self.iter()
}
fn controller_id(self) -> Endpoint {
self.controller_id
}
}
impl IntoIterator for NodeCluster {
type Item = GlobalNode;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter {
idx: 0..self.nodes.len(),
inner: self,
}
}
}
impl NodeCluster {
pub fn new(controller_id: Endpoint, nodes: impl IntoIterator<Item = Node>) -> Self {
Self {
controller_id,
nodes: nodes.into_iter().collect::<Vec<_>>().into(),
}
}
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.nodes.iter(),
}
}
pub fn controller_id(&self) -> Endpoint {
self.controller_id
}
pub fn nodes(&self) -> &[Node] {
&self.nodes
}
pub fn try_from_iter(iter: impl IntoIterator<Item = GlobalNode>) -> Option<Self> {
let mut it = iter.into_iter();
let first = it.next()?;
let ctrl_id = first.controller_id();
Some(Self {
controller_id: ctrl_id,
nodes: std::iter::once(Some(first.local_node()))
.chain(it.map(|n| (n.controller_id() == ctrl_id).then_some(n.local_node())))
.collect::<Option<Box<_>>>()?,
})
}
}
}
pub use node_cluster::NodeCluster;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct GlobalEdge {
index: EdgeIndex,
controller_id: Endpoint,
}
impl GlobalEdge {
pub fn controller_id(self) -> Endpoint {
self.controller_id
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Allocative)]
pub struct NodeInfo {
pub at: CallString,
pub description: String,
pub span: Span,
}
impl Display for NodeInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} @ {}", self.description, self.at)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Allocative)]
pub struct EdgeInfo {
pub kind: EdgeKind,
pub at: CallString,
pub source_use: SourceUse,
pub target_use: TargetUse,
}
impl Display for EdgeInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.at, self.kind)
}
}
impl EdgeInfo {
pub fn is_data(&self) -> bool {
matches!(self.kind, EdgeKind::Data)
}
pub fn is_control(&self) -> bool {
matches!(self.kind, EdgeKind::Control)
}
}
#[derive(
Clone,
Debug,
Copy,
Eq,
PartialEq,
Deserialize,
Serialize,
strum::EnumIs,
strum::Display,
Allocative,
)]
pub enum EdgeKind {
Data,
Control,
}
pub type SPDGImpl = petgraph::Graph<NodeInfo, EdgeInfo>;
#[derive(Clone, Serialize, Deserialize, Debug, Allocative)]
pub struct SPDG {
pub name: Identifier,
pub path: Box<[Identifier]>,
#[cfg_attr(feature = "rustc", serde(with = "rustc_proxies::DefId"))]
#[allocative(visit = allocative_visit_simple_sized)]
pub id: Endpoint,
#[allocative(visit = allocative_visit_petgraph_graph)]
pub graph: SPDGImpl,
#[allocative(visit = allocative_visit_map_coerce_key)]
pub markers: HashMap<Node, Box<[Identifier]>>,
#[allocative(visit = allocative_visit_box_slice_simple_t)]
pub arguments: Box<[Node]>,
#[allocative(visit = allocative_visit_box_slice_simple_t)]
pub return_: Box<[Node]>,
#[allocative(visit = allocative_visit_map_coerce_key)]
pub type_assigns: HashMap<Node, Types>,
pub statistics: SPDGStats,
}
fn allocative_visit_petgraph_graph<
N: Allocative,
E: Allocative,
Ty: petgraph::EdgeType,
Ix: petgraph::csr::IndexType,
>(
graph: &petgraph::Graph<N, E, Ty, Ix>,
visitor: &mut allocative::Visitor<'_>,
) {
#[repr(transparent)]
struct EdgeProxy<E, Ix>(petgraph::graph::Edge<E, Ix>);
impl<E: Allocative, Ix> Allocative for EdgeProxy<E, Ix> {
fn visit<'a, 'b: 'a>(&self, visitor: &'a mut allocative::Visitor<'b>) {
let mut visitor = visitor.enter_self_sized::<Self>();
visitor.visit_field_with(ident_key!(value), std::mem::size_of::<Self>(), |visitor| {
let mut visitor = visitor.enter_self_sized::<petgraph::graph::Edge<E, Ix>>();
visitor.visit_field(ident_key!(weight), &self.0.weight);
visitor.exit()
});
visitor.exit();
}
}
#[repr(transparent)]
struct NodeProxy<N, Ix>(petgraph::graph::Node<N, Ix>);
impl<N: Allocative, Ix> Allocative for NodeProxy<N, Ix> {
fn visit<'a, 'b: 'a>(&self, visitor: &'a mut allocative::Visitor<'b>) {
let mut visitor = visitor.enter_self_sized::<Self>();
visitor.visit_field_with(ident_key!(value), std::mem::size_of::<Self>(), |visitor| {
let mut visitor = visitor.enter_self_sized::<petgraph::graph::Node<N, Ix>>();
visitor.visit_field(ident_key!(weight), &self.0.weight);
visitor.exit()
});
visitor.exit();
}
}
let (ncap, ecap) = graph.capacity();
let mut visitor = visitor.enter_self_sized::<petgraph::Graph<N, E, Ty, Ix>>();
let edges_as_proxy: &[EdgeProxy<E, Ix>] = unsafe {
std::mem::transmute::<&[petgraph::graph::Edge<E, Ix>], &[EdgeProxy<E, Ix>]>(
graph.raw_edges(),
)
};
let nodes_as_proxy = unsafe {
std::mem::transmute::<&[petgraph::graph::Node<N, Ix>], &[NodeProxy<N, Ix>]>(
graph.raw_nodes(),
)
};
visitor.visit_field_with(
ident_key!(nodes),
std::mem::size_of::<Vec<petgraph::graph::Node<N, Ix>>>(),
|visitor| {
visitor.visit_vec_like_body(nodes_as_proxy, ncap);
},
);
visitor.visit_field_with(
ident_key!(edges),
std::mem::size_of::<Vec<petgraph::graph::Edge<E, Ix>>>(),
|visitor| {
visitor.visit_vec_like_body(edges_as_proxy, ecap);
},
);
visitor.exit()
}
#[derive(Clone, Serialize, Deserialize, Debug, Default, Allocative)]
pub struct SPDGStats {
pub unique_locs: u32,
pub unique_functions: u32,
pub analyzed_locs: u32,
pub analyzed_functions: u32,
pub inlinings_performed: u32,
pub construction_time: Duration,
pub conversion_time: Duration,
}
#[derive(Clone, Serialize, Deserialize, Debug, Default, Allocative)]
pub struct Types(
#[cfg_attr(feature = "rustc", serde(with = "ser_defid_seq"))]
#[allocative(visit = allocative_visit_box_slice_simple_t)]
pub Box<[TypeId]>,
);
impl SPDG {
pub fn node_info(&self, node: Node) -> &NodeInfo {
self.graph.node_weight(node).unwrap()
}
pub fn data_sinks(&self) -> impl Iterator<Item = Node> + '_ {
self.graph
.edge_references()
.filter(|e| e.weight().is_data())
.map(|e| e.target())
.unique()
}
pub fn edges(&self) -> impl Iterator<Item = EdgeReference<'_, EdgeInfo>> + '_ {
self.graph.edge_references()
}
pub fn all_sources(&self) -> impl Iterator<Item = Node> + '_ {
self.graph.node_identifiers().map(Into::into)
}
pub fn dump_dot(&self, mut out: impl std::io::Write) -> std::io::Result<()> {
use petgraph::dot::Dot;
let dot = Dot::with_config(&self.graph, &[]);
write!(out, "{dot}")
}
pub fn arguments(&self) -> NodeCluster {
NodeCluster::new(self.id, self.arguments.iter().copied())
}
pub fn node_types(&self, node: Node) -> &[TypeId] {
self.type_assigns.get(&node).map_or(&[], |r| &r.0)
}
}
#[derive(Clone)]
pub struct DisplayNode<'a> {
node: NodeIndex,
graph: &'a SPDG,
detailed: bool,
}
impl<'a> DisplayNode<'a> {
pub fn pretty(node: NodeIndex, graph: &'a SPDG) -> Self {
Self {
node,
graph,
detailed: true,
}
}
pub fn simple(node: NodeIndex, graph: &'a SPDG) -> Self {
Self {
node,
graph,
detailed: false,
}
}
}
impl Display for DisplayNode<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let weight = self.graph.graph.node_weight(self.node).unwrap();
if self.detailed {
write!(
f,
"{{{}}} {} @ {}",
self.node.index(),
weight.description,
weight.at
)
} else {
write!(f, "{{{}}} {}", self.node.index(), weight.description)
}
}
}