use std::collections::HashMap;
use std::fmt::Display;
use std::num::NonZeroU32;
use std::ops::Index;
use std::ops::IndexMut;
use controlled_option::ControlledOption;
use either::Either;
use fxhash::FxHashMap;
use smallvec::SmallVec;
use crate::arena::Arena;
use crate::arena::Handle;
use crate::arena::SupplementalArena;
#[repr(C)]
struct InternedStringContent {
start: *const u8,
len: usize,
}
const INITIAL_STRING_CAPACITY: usize = 512;
struct InternedStringArena {
current_buffer: Vec<u8>,
full_buffers: Vec<Vec<u8>>,
}
impl InternedStringArena {
fn new() -> InternedStringArena {
InternedStringArena {
current_buffer: Vec::with_capacity(INITIAL_STRING_CAPACITY),
full_buffers: Vec::new(),
}
}
fn add(&mut self, value: &str) -> InternedStringContent {
let value = value.as_bytes();
let len = value.len();
let capacity = self.current_buffer.capacity();
let remaining_capacity = capacity - self.current_buffer.len();
if len > remaining_capacity {
let new_capacity = (capacity.max(len) + 1).next_power_of_two();
let new_buffer = Vec::with_capacity(new_capacity);
let old_buffer = std::mem::replace(&mut self.current_buffer, new_buffer);
self.full_buffers.push(old_buffer);
}
let start_index = self.current_buffer.len();
self.current_buffer.extend_from_slice(value);
let start = unsafe { self.current_buffer.as_ptr().add(start_index) };
InternedStringContent { start, len }
}
}
impl InternedStringContent {
fn as_str(&self) -> &str {
unsafe {
let bytes = std::slice::from_raw_parts(self.start, self.len);
std::str::from_utf8_unchecked(bytes)
}
}
unsafe fn as_hash_key(&self) -> &'static str {
let bytes = std::slice::from_raw_parts(self.start, self.len);
std::str::from_utf8_unchecked(bytes)
}
}
unsafe impl Send for InternedStringContent {}
unsafe impl Sync for InternedStringContent {}
#[repr(C)]
pub struct Symbol {
content: InternedStringContent,
}
impl Symbol {
pub fn as_str(&self) -> &str {
self.content.as_str()
}
}
impl PartialEq<&str> for Symbol {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl StackGraph {
pub fn add_symbol<S: AsRef<str> + ?Sized>(&mut self, symbol: &S) -> Handle<Symbol> {
let symbol = symbol.as_ref();
if let Some(handle) = self.symbol_handles.get(symbol) {
return *handle;
}
let interned = self.interned_strings.add(symbol);
let hash_key = unsafe { interned.as_hash_key() };
let handle = self.symbols.add(Symbol { content: interned });
self.symbol_handles.insert(hash_key, handle);
handle
}
pub fn iter_symbols(&self) -> impl Iterator<Item = Handle<Symbol>> {
self.symbols.iter_handles()
}
}
impl Index<Handle<Symbol>> for StackGraph {
type Output = str;
#[inline(always)]
fn index(&self, handle: Handle<Symbol>) -> &str {
self.symbols.get(handle).as_str()
}
}
#[doc(hidden)]
pub struct DisplaySymbol<'a> {
wrapped: Handle<Symbol>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplaySymbol<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &self.graph[self.wrapped])
}
}
impl Handle<Symbol> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplaySymbol {
wrapped: self,
graph,
}
}
}
#[repr(C)]
pub struct InternedString {
content: InternedStringContent,
}
impl InternedString {
fn as_str(&self) -> &str {
self.content.as_str()
}
}
impl PartialEq<&str> for InternedString {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl StackGraph {
pub fn add_string<S: AsRef<str> + ?Sized>(&mut self, string: &S) -> Handle<InternedString> {
let string = string.as_ref();
if let Some(handle) = self.string_handles.get(string) {
return *handle;
}
let interned = self.interned_strings.add(string);
let hash_key = unsafe { interned.as_hash_key() };
let handle = self.strings.add(InternedString { content: interned });
self.string_handles.insert(hash_key, handle);
handle
}
pub fn iter_strings(&self) -> impl Iterator<Item = Handle<InternedString>> {
self.strings.iter_handles()
}
}
impl Index<Handle<InternedString>> for StackGraph {
type Output = str;
#[inline(always)]
fn index(&self, handle: Handle<InternedString>) -> &str {
self.strings.get(handle).as_str()
}
}
#[doc(hidden)]
pub struct DisplayInternedString<'a> {
wrapped: Handle<InternedString>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayInternedString<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &self.graph[self.wrapped])
}
}
impl Handle<InternedString> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayInternedString {
wrapped: self,
graph,
}
}
}
pub struct File {
name: InternedStringContent,
}
impl File {
pub fn name(&self) -> &str {
self.name.as_str()
}
}
impl StackGraph {
pub fn add_file<S: AsRef<str> + ?Sized>(
&mut self,
name: &S,
) -> Result<Handle<File>, Handle<File>> {
let name = name.as_ref();
if let Some(handle) = self.file_handles.get(name) {
return Err(*handle);
}
let interned = self.interned_strings.add(name);
let hash_key = unsafe { interned.as_hash_key() };
let handle = self.files.add(File { name: interned });
self.file_handles.insert(hash_key, handle);
Ok(handle)
}
#[inline(always)]
pub fn get_or_create_file<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> Handle<File> {
self.add_file(name).unwrap_or_else(|handle| handle)
}
pub fn get_file<S: AsRef<str> + ?Sized>(&self, name: &S) -> Option<Handle<File>> {
let name = name.as_ref();
self.file_handles.get(name).copied()
}
}
impl StackGraph {
pub fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
self.node_id_handles.nodes_for_file(file)
}
pub fn iter_files(&self) -> impl Iterator<Item = Handle<File>> + '_ {
self.files.iter_handles()
}
}
impl Display for File {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
impl Index<Handle<File>> for StackGraph {
type Output = File;
#[inline(always)]
fn index(&self, handle: Handle<File>) -> &File {
&self.files.get(handle)
}
}
#[doc(hidden)]
pub struct DisplayFile<'a> {
wrapped: Handle<File>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayFile<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.graph[self.wrapped])
}
}
impl Handle<File> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayFile {
wrapped: self,
graph,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NodeID {
file: ControlledOption<Handle<File>>,
local_id: u32,
}
pub(crate) const ROOT_NODE_ID: u32 = 1;
pub(crate) const JUMP_TO_NODE_ID: u32 = 2;
impl NodeID {
#[inline(always)]
pub fn root() -> NodeID {
NodeID {
file: ControlledOption::none(),
local_id: ROOT_NODE_ID,
}
}
#[inline(always)]
pub fn jump_to() -> NodeID {
NodeID {
file: ControlledOption::none(),
local_id: JUMP_TO_NODE_ID,
}
}
#[inline(always)]
pub fn new_in_file(file: Handle<File>, local_id: u32) -> NodeID {
NodeID {
file: ControlledOption::some(file),
local_id,
}
}
#[inline(always)]
pub fn is_root(self) -> bool {
self.file.is_none() && self.local_id == ROOT_NODE_ID
}
#[inline(always)]
pub fn is_jump_to(self) -> bool {
self.file.is_none() && self.local_id == JUMP_TO_NODE_ID
}
#[inline(always)]
pub fn file(self) -> Option<Handle<File>> {
self.file.into()
}
#[inline(always)]
pub fn local_id(self) -> u32 {
self.local_id
}
#[inline(always)]
pub fn is_in_file(self, file: Handle<File>) -> bool {
match self.file.into_option() {
Some(this_file) => this_file == file,
_ => true,
}
}
}
#[doc(hidden)]
pub struct DisplayNodeID<'a> {
wrapped: NodeID,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayNodeID<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.wrapped.file.into_option() {
Some(file) => write!(f, "{}({})", file.display(self.graph), self.wrapped.local_id),
None => {
if self.wrapped.is_root() {
write!(f, "[root]")
} else if self.wrapped.is_jump_to() {
write!(f, "[jump]")
} else {
unreachable!();
}
}
}
}
}
impl NodeID {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayNodeID {
wrapped: self,
graph,
}
}
}
#[repr(C)]
pub enum Node {
DropScopes(DropScopesNode),
JumpTo(JumpToNode),
PopScopedSymbol(PopScopedSymbolNode),
PopSymbol(PopSymbolNode),
PushScopedSymbol(PushScopedSymbolNode),
PushSymbol(PushSymbolNode),
Root(RootNode),
Scope(ScopeNode),
}
impl Node {
#[inline(always)]
pub fn is_exported_scope(&self) -> bool {
match self {
Node::Scope(node) => node.is_exported,
_ => false,
}
}
#[inline(always)]
pub fn is_definition(&self) -> bool {
match self {
Node::PopScopedSymbol(node) => node.is_definition,
Node::PopSymbol(node) => node.is_definition,
_ => false,
}
}
#[inline(always)]
pub fn is_reference(&self) -> bool {
match self {
Node::PushScopedSymbol(node) => node.is_reference,
Node::PushSymbol(node) => node.is_reference,
_ => false,
}
}
#[inline(always)]
pub fn is_jump_to(&self) -> bool {
matches!(self, Node::JumpTo(_))
}
#[inline(always)]
pub fn is_root(&self) -> bool {
matches!(self, Node::Root(_))
}
#[inline(always)]
pub fn is_endpoint(&self) -> bool {
self.is_definition() || self.is_exported_scope() || self.is_reference() || self.is_root()
}
pub fn symbol(&self) -> Option<Handle<Symbol>> {
match self {
Node::PushScopedSymbol(node) => Some(node.symbol),
Node::PushSymbol(node) => Some(node.symbol),
Node::PopScopedSymbol(node) => Some(node.symbol),
Node::PopSymbol(node) => Some(node.symbol),
_ => None,
}
}
pub fn scope(&self) -> Option<NodeID> {
match self {
Node::PushScopedSymbol(node) => Some(node.scope),
_ => None,
}
}
pub fn id(&self) -> NodeID {
match self {
Node::DropScopes(node) => node.id,
Node::JumpTo(node) => node.id,
Node::PushScopedSymbol(node) => node.id,
Node::PushSymbol(node) => node.id,
Node::PopScopedSymbol(node) => node.id,
Node::PopSymbol(node) => node.id,
Node::Root(node) => node.id,
Node::Scope(node) => node.id,
}
}
#[inline(always)]
pub fn file(&self) -> Option<Handle<File>> {
self.id().file()
}
#[inline(always)]
pub fn is_in_file(&self, file: Handle<File>) -> bool {
self.id().is_in_file(file)
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayNode {
wrapped: self,
graph,
}
}
}
impl StackGraph {
#[inline(always)]
pub fn jump_to_node() -> Handle<Node> {
Handle::new(unsafe { NonZeroU32::new_unchecked(2) })
}
#[inline(always)]
pub fn root_node() -> Handle<Node> {
Handle::new(unsafe { NonZeroU32::new_unchecked(1) })
}
pub fn new_node_id(&mut self, file: Handle<File>) -> NodeID {
self.node_id_handles.unused_id(file)
}
pub fn iter_nodes(&self) -> impl Iterator<Item = Handle<Node>> {
self.nodes.iter_handles()
}
pub fn node_for_id(&self, id: NodeID) -> Option<Handle<Node>> {
if id.file().is_some() {
self.node_id_handles.try_handle_for_id(id)
} else if id.is_root() {
Some(StackGraph::root_node())
} else if id.is_jump_to() {
Some(StackGraph::jump_to_node())
} else {
None
}
}
pub(crate) fn add_node(&mut self, id: NodeID, node: Node) -> Option<Handle<Node>> {
if let Some(_) = self.node_id_handles.handle_for_id(id) {
return None;
}
let handle = self.nodes.add(node);
self.node_id_handles.set_handle_for_id(id, handle);
Some(handle)
}
pub(crate) fn get_or_create_node(&mut self, id: NodeID, node: Node) -> Handle<Node> {
if let Some(handle) = self.node_id_handles.handle_for_id(id) {
return handle;
}
let handle = self.nodes.add(node);
self.node_id_handles.set_handle_for_id(id, handle);
handle
}
}
#[doc(hidden)]
pub struct DisplayNode<'a> {
wrapped: &'a Node,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.wrapped {
Node::DropScopes(node) => node.display(self.graph).fmt(f),
Node::JumpTo(node) => node.fmt(f),
Node::PushScopedSymbol(node) => node.display(self.graph).fmt(f),
Node::PushSymbol(node) => node.display(self.graph).fmt(f),
Node::PopScopedSymbol(node) => node.display(self.graph).fmt(f),
Node::PopSymbol(node) => node.display(self.graph).fmt(f),
Node::Root(node) => node.fmt(f),
Node::Scope(node) => node.display(self.graph).fmt(f),
}
}
}
impl Handle<Node> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayNode {
wrapped: &graph[self],
graph,
}
}
}
impl Index<Handle<Node>> for StackGraph {
type Output = Node;
#[inline(always)]
fn index(&self, handle: Handle<Node>) -> &Node {
self.nodes.get(handle)
}
}
impl IndexMut<Handle<Node>> for StackGraph {
#[inline(always)]
fn index_mut(&mut self, handle: Handle<Node>) -> &mut Node {
self.nodes.get_mut(handle)
}
}
#[repr(C)]
pub struct DropScopesNode {
pub id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_endpoint: bool,
}
impl From<DropScopesNode> for Node {
fn from(node: DropScopesNode) -> Node {
Node::DropScopes(node)
}
}
impl StackGraph {
pub fn add_drop_scopes_node(&mut self, id: NodeID) -> Option<Handle<Node>> {
let node = DropScopesNode {
id,
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_endpoint: false,
};
self.add_node(id, node.into())
}
}
impl DropScopesNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayDropScopesNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayDropScopesNode<'a> {
wrapped: &'a DropScopesNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayDropScopesNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(f, "[{} drop scopes]", self.wrapped.id.display(self.graph))
}
}
}
#[repr(C)]
pub struct JumpToNode {
id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_endpoint: bool,
}
impl From<JumpToNode> for Node {
fn from(node: JumpToNode) -> Node {
Node::JumpTo(node)
}
}
impl JumpToNode {
fn new() -> JumpToNode {
JumpToNode {
id: NodeID::jump_to(),
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_endpoint: false,
}
}
}
impl Display for JumpToNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[jump to scope]")
}
}
#[repr(C)]
pub struct PopScopedSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
_scope: NodeID,
pub is_definition: bool,
}
impl From<PopScopedSymbolNode> for Node {
fn from(node: PopScopedSymbolNode) -> Node {
Node::PopScopedSymbol(node)
}
}
impl StackGraph {
pub fn add_pop_scoped_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
is_definition: bool,
) -> Option<Handle<Node>> {
let node = PopScopedSymbolNode {
id,
symbol,
_scope: NodeID::default(),
is_definition,
};
self.add_node(id, node.into())
}
}
impl PopScopedSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPopScopedSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPopScopedSymbolNode<'a> {
wrapped: &'a PopScopedSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPopScopedSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_definition {
"scoped definition"
} else {
"pop scoped"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct PopSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
_scope: NodeID,
pub is_definition: bool,
}
impl From<PopSymbolNode> for Node {
fn from(node: PopSymbolNode) -> Node {
Node::PopSymbol(node)
}
}
impl StackGraph {
pub fn add_pop_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
is_definition: bool,
) -> Option<Handle<Node>> {
let node = PopSymbolNode {
id,
symbol,
_scope: NodeID::default(),
is_definition,
};
self.add_node(id, node.into())
}
}
impl PopSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPopSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPopSymbolNode<'a> {
wrapped: &'a PopSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPopSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_definition {
"definition"
} else {
"pop"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct PushScopedSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
pub scope: NodeID,
pub is_reference: bool,
_phantom: (),
}
impl From<PushScopedSymbolNode> for Node {
fn from(node: PushScopedSymbolNode) -> Node {
Node::PushScopedSymbol(node)
}
}
impl StackGraph {
pub fn add_push_scoped_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
scope: NodeID,
is_reference: bool,
) -> Option<Handle<Node>> {
let node = PushScopedSymbolNode {
id,
symbol,
scope,
is_reference,
_phantom: (),
};
self.add_node(id, node.into())
}
}
impl PushScopedSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPushScopedSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPushScopedSymbolNode<'a> {
wrapped: &'a PushScopedSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPushScopedSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_reference {
"scoped reference"
} else {
"push scoped"
},
self.wrapped.symbol.display(self.graph),
self.wrapped.scope.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct PushSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
_scope: NodeID,
pub is_reference: bool,
}
impl From<PushSymbolNode> for Node {
fn from(node: PushSymbolNode) -> Node {
Node::PushSymbol(node)
}
}
impl StackGraph {
pub fn add_push_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
is_reference: bool,
) -> Option<Handle<Node>> {
let node = PushSymbolNode {
id,
symbol,
_scope: NodeID::default(),
is_reference,
};
self.add_node(id, node.into())
}
}
impl PushSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPushSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPushSymbolNode<'a> {
wrapped: &'a PushSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPushSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_reference {
"reference"
} else {
"push"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct RootNode {
id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_endpoint: bool,
}
impl From<RootNode> for Node {
fn from(node: RootNode) -> Node {
Node::Root(node)
}
}
impl RootNode {
fn new() -> RootNode {
RootNode {
id: NodeID::root(),
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_endpoint: false,
}
}
}
impl Display for RootNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[root]")
}
}
struct NodeIDHandles {
files: SupplementalArena<File, Vec<Option<Handle<Node>>>>,
}
impl NodeIDHandles {
fn new() -> NodeIDHandles {
NodeIDHandles {
files: SupplementalArena::new(),
}
}
fn try_handle_for_id(&self, node_id: NodeID) -> Option<Handle<Node>> {
let file_entry = self.files.get(node_id.file().unwrap())?;
let node_index = node_id.local_id as usize;
if node_index >= file_entry.len() {
return None;
}
file_entry[node_index]
}
fn handle_for_id(&mut self, node_id: NodeID) -> Option<Handle<Node>> {
let file_entry = &mut self.files[node_id.file().unwrap()];
let node_index = node_id.local_id as usize;
if node_index >= file_entry.len() {
file_entry.resize(node_index + 1, None);
}
file_entry[node_index]
}
fn set_handle_for_id(&mut self, node_id: NodeID, handle: Handle<Node>) {
let file_entry = &mut self.files[node_id.file().unwrap()];
let node_index = node_id.local_id as usize;
file_entry[node_index] = Some(handle);
}
fn unused_id(&mut self, file: Handle<File>) -> NodeID {
let local_id = self
.files
.get(file)
.map(|file_entry| file_entry.len() as u32)
.unwrap_or(0);
NodeID::new_in_file(file, local_id)
}
fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
let file_entry = match self.files.get(file) {
Some(file_entry) => file_entry,
None => return Either::Left(std::iter::empty()),
};
Either::Right(file_entry.iter().filter_map(|entry| *entry))
}
}
#[repr(C)]
pub struct ScopeNode {
pub id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
pub is_exported: bool,
}
impl From<ScopeNode> for Node {
fn from(node: ScopeNode) -> Node {
Node::Scope(node)
}
}
impl StackGraph {
pub fn add_scope_node(&mut self, id: NodeID, is_exported: bool) -> Option<Handle<Node>> {
let node = ScopeNode {
id,
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
is_exported,
};
self.add_node(id, node.into())
}
}
impl ScopeNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayScopeNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayScopeNode<'a> {
wrapped: &'a ScopeNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayScopeNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{}{} scope]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_exported {
" exported"
} else {
""
},
)
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Edge {
pub source: Handle<Node>,
pub sink: Handle<Node>,
pub precedence: i32,
}
pub(crate) struct OutgoingEdge {
sink: Handle<Node>,
precedence: i32,
}
impl StackGraph {
pub fn add_edge(&mut self, source: Handle<Node>, sink: Handle<Node>, precedence: i32) {
let edges = &mut self.outgoing_edges[source];
if let Err(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
edges.insert(index, OutgoingEdge { sink, precedence });
self.incoming_edges[sink] += Degree::One;
}
}
pub fn set_edge_precedence(
&mut self,
source: Handle<Node>,
sink: Handle<Node>,
precedence: i32,
) {
let edges = &mut self.outgoing_edges[source];
if let Ok(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
edges[index].precedence = precedence;
}
}
pub fn outgoing_edges(&self, source: Handle<Node>) -> impl Iterator<Item = Edge> + '_ {
match self.outgoing_edges.get(source) {
Some(edges) => Either::Right(edges.iter().map(move |o| Edge {
source,
sink: o.sink,
precedence: o.precedence,
})),
None => Either::Left(std::iter::empty()),
}
}
pub fn incoming_edge_degree(&self, sink: Handle<Node>) -> Degree {
self.incoming_edges
.get(sink)
.cloned()
.unwrap_or(Degree::Zero)
}
}
#[repr(C)]
#[derive(Default)]
pub struct SourceInfo {
pub span: lsp_positions::Span,
pub syntax_type: ControlledOption<Handle<InternedString>>,
pub containing_line: ControlledOption<Handle<InternedString>>,
pub definiens_span: lsp_positions::Span,
pub fully_qualified_name: ControlledOption<Handle<InternedString>>,
}
impl StackGraph {
pub fn source_info(&self, node: Handle<Node>) -> Option<&SourceInfo> {
self.source_info.get(node)
}
pub fn source_info_mut(&mut self, node: Handle<Node>) -> &mut SourceInfo {
&mut self.source_info[node]
}
}
#[derive(Default)]
pub struct DebugInfo {
entries: Vec<DebugEntry>,
}
impl DebugInfo {
pub fn add(&mut self, key: Handle<InternedString>, value: Handle<InternedString>) {
self.entries.push(DebugEntry { key, value });
}
pub fn iter(&self) -> std::slice::Iter<DebugEntry> {
self.entries.iter()
}
}
pub struct DebugEntry {
pub key: Handle<InternedString>,
pub value: Handle<InternedString>,
}
impl StackGraph {
pub fn node_debug_info(&self, node: Handle<Node>) -> Option<&DebugInfo> {
self.node_debug_info.get(node)
}
pub fn node_debug_info_mut(&mut self, node: Handle<Node>) -> &mut DebugInfo {
&mut self.node_debug_info[node]
}
pub fn edge_debug_info(&self, source: Handle<Node>, sink: Handle<Node>) -> Option<&DebugInfo> {
self.edge_debug_info.get(source).and_then(|es| {
match es.binary_search_by_key(&sink, |e| e.0) {
Ok(idx) => Some(&es[idx].1),
Err(_) => None,
}
})
}
pub fn edge_debug_info_mut(
&mut self,
source: Handle<Node>,
sink: Handle<Node>,
) -> &mut DebugInfo {
let es = &mut self.edge_debug_info[source];
let idx = match es.binary_search_by_key(&sink, |e| e.0) {
Ok(idx) => idx,
Err(idx) => {
es.insert(idx, (sink, DebugInfo::default()));
idx
}
};
&mut es[idx].1
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Degree {
Zero,
One,
Multiple,
}
impl Default for Degree {
fn default() -> Self {
Self::Zero
}
}
impl std::ops::Add for Degree {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Zero, result) | (result, Self::Zero) => result,
_ => Self::Multiple,
}
}
}
impl std::ops::AddAssign for Degree {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
pub struct StackGraph {
interned_strings: InternedStringArena,
pub(crate) symbols: Arena<Symbol>,
symbol_handles: FxHashMap<&'static str, Handle<Symbol>>,
pub(crate) strings: Arena<InternedString>,
string_handles: FxHashMap<&'static str, Handle<InternedString>>,
pub(crate) files: Arena<File>,
file_handles: FxHashMap<&'static str, Handle<File>>,
pub(crate) nodes: Arena<Node>,
pub(crate) source_info: SupplementalArena<Node, SourceInfo>,
node_id_handles: NodeIDHandles,
outgoing_edges: SupplementalArena<Node, SmallVec<[OutgoingEdge; 4]>>,
incoming_edges: SupplementalArena<Node, Degree>,
pub(crate) node_debug_info: SupplementalArena<Node, DebugInfo>,
pub(crate) edge_debug_info: SupplementalArena<Node, SmallVec<[(Handle<Node>, DebugInfo); 4]>>,
}
impl StackGraph {
pub fn new() -> StackGraph {
StackGraph::default()
}
pub fn add_from_graph(
&mut self,
other: &StackGraph,
) -> Result<Vec<Handle<File>>, Handle<File>> {
let mut files = HashMap::new();
for other_file in other.iter_files() {
let file = self.add_file(other[other_file].name())?;
files.insert(other_file, file);
}
let files = files;
let node_id = |other_node_id: NodeID| {
if other_node_id.is_root() {
NodeID::root()
} else if other_node_id.is_jump_to() {
NodeID::jump_to()
} else {
NodeID::new_in_file(
files[&other_node_id.file.into_option().unwrap()],
other_node_id.local_id,
)
}
};
let mut nodes = HashMap::new();
nodes.insert(Self::root_node(), Self::root_node());
nodes.insert(Self::jump_to_node(), Self::jump_to_node());
for other_file in files.keys().cloned() {
let file = files[&other_file];
for other_node in other.nodes_for_file(other_file) {
let value: Node = match other[other_node] {
Node::DropScopes(DropScopesNode { id, .. }) => DropScopesNode {
id: NodeID::new_in_file(file, id.local_id),
_symbol: ControlledOption::default(),
_scope: NodeID::default(),
_is_endpoint: bool::default(),
}
.into(),
Node::JumpTo(JumpToNode { .. }) => JumpToNode {
id: NodeID::jump_to(),
_symbol: ControlledOption::default(),
_scope: NodeID::default(),
_is_endpoint: bool::default(),
}
.into(),
Node::PopScopedSymbol(PopScopedSymbolNode {
id,
symbol,
is_definition,
..
}) => PopScopedSymbolNode {
id: NodeID::new_in_file(file, id.local_id),
symbol: self.add_symbol(&other[symbol]),
_scope: NodeID::default(),
is_definition: is_definition,
}
.into(),
Node::PopSymbol(PopSymbolNode {
id,
symbol,
is_definition,
..
}) => PopSymbolNode {
id: NodeID::new_in_file(file, id.local_id),
symbol: self.add_symbol(&other[symbol]),
_scope: NodeID::default(),
is_definition: is_definition,
}
.into(),
Node::PushScopedSymbol(PushScopedSymbolNode {
id,
symbol,
scope,
is_reference,
..
}) => PushScopedSymbolNode {
id: NodeID::new_in_file(file, id.local_id),
symbol: self.add_symbol(&other[symbol]),
scope: node_id(scope),
is_reference: is_reference,
_phantom: (),
}
.into(),
Node::PushSymbol(PushSymbolNode {
id,
symbol,
is_reference,
..
}) => PushSymbolNode {
id: NodeID::new_in_file(file, id.local_id),
symbol: self.add_symbol(&other[symbol]),
_scope: NodeID::default(),
is_reference: is_reference,
}
.into(),
Node::Root(RootNode { .. }) => RootNode {
id: NodeID::root(),
_symbol: ControlledOption::default(),
_scope: NodeID::default(),
_is_endpoint: bool::default(),
}
.into(),
Node::Scope(ScopeNode {
id, is_exported, ..
}) => ScopeNode {
id: NodeID::new_in_file(file, id.local_id),
_symbol: ControlledOption::default(),
_scope: NodeID::default(),
is_exported: is_exported,
}
.into(),
};
let node = self.add_node(value.id(), value).unwrap();
nodes.insert(other_node, node);
if let Some(source_info) = other.source_info(other_node) {
*self.source_info_mut(node) = SourceInfo {
span: source_info.span.clone(),
syntax_type: source_info
.syntax_type
.into_option()
.map(|st| self.add_string(&other[st]))
.into(),
containing_line: source_info
.containing_line
.into_option()
.map(|cl| self.add_string(&other[cl]))
.into(),
definiens_span: source_info.definiens_span.clone(),
fully_qualified_name: ControlledOption::default(),
};
}
if let Some(debug_info) = other.node_debug_info(other_node) {
*self.node_debug_info_mut(node) = DebugInfo {
entries: debug_info
.entries
.iter()
.map(|e| DebugEntry {
key: self.add_string(&other[e.key]),
value: self.add_string(&other[e.value]),
})
.collect::<Vec<_>>(),
};
}
}
for other_node in nodes.keys().cloned() {
for other_edge in other.outgoing_edges(other_node) {
self.add_edge(
nodes[&other_edge.source],
nodes[&other_edge.sink],
other_edge.precedence,
);
}
}
}
Ok(files.into_values().collect())
}
}
impl Default for StackGraph {
fn default() -> StackGraph {
let mut nodes = Arena::new();
nodes.add(RootNode::new().into());
nodes.add(JumpToNode::new().into());
StackGraph {
interned_strings: InternedStringArena::new(),
symbols: Arena::new(),
symbol_handles: FxHashMap::default(),
strings: Arena::new(),
string_handles: FxHashMap::default(),
files: Arena::new(),
file_handles: FxHashMap::default(),
nodes,
source_info: SupplementalArena::new(),
node_id_handles: NodeIDHandles::new(),
outgoing_edges: SupplementalArena::new(),
incoming_edges: SupplementalArena::new(),
node_debug_info: SupplementalArena::new(),
edge_debug_info: SupplementalArena::new(),
}
}
}