1use std::collections::HashMap;
53use std::fmt::Display;
54use std::num::NonZeroU32;
55use std::ops::Index;
56use std::ops::IndexMut;
57
58use controlled_option::ControlledOption;
59use either::Either;
60use fxhash::FxHashMap;
61use smallvec::SmallVec;
62
63use crate::arena::Arena;
64use crate::arena::Handle;
65use crate::arena::SupplementalArena;
66
67#[repr(C)]
71struct InternedStringContent {
72 start: *const u8,
74 len: usize,
75}
76
77const INITIAL_STRING_CAPACITY: usize = 512;
78
79struct InternedStringArena {
89 current_buffer: Vec<u8>,
90 full_buffers: Vec<Vec<u8>>,
91}
92
93impl InternedStringArena {
94 fn new() -> InternedStringArena {
95 InternedStringArena {
96 current_buffer: Vec::with_capacity(INITIAL_STRING_CAPACITY),
97 full_buffers: Vec::new(),
98 }
99 }
100
101 fn add(&mut self, value: &str) -> InternedStringContent {
104 let value = value.as_bytes();
106 let len = value.len();
107 let capacity = self.current_buffer.capacity();
108 let remaining_capacity = capacity - self.current_buffer.len();
109 if len > remaining_capacity {
110 let new_capacity = (capacity.max(len) + 1).next_power_of_two();
114 let new_buffer = Vec::with_capacity(new_capacity);
115 let old_buffer = std::mem::replace(&mut self.current_buffer, new_buffer);
116 self.full_buffers.push(old_buffer);
117 }
118
119 let start_index = self.current_buffer.len();
123 self.current_buffer.extend_from_slice(value);
124 let start = unsafe { self.current_buffer.as_ptr().add(start_index) };
125 InternedStringContent { start, len }
126 }
127}
128
129impl InternedStringContent {
130 fn as_str(&self) -> &str {
135 unsafe {
136 let bytes = std::slice::from_raw_parts(self.start, self.len);
137 std::str::from_utf8_unchecked(bytes)
138 }
139 }
140
141 unsafe fn as_hash_key(&self) -> &'static str {
148 let bytes = std::slice::from_raw_parts(self.start, self.len);
149 std::str::from_utf8_unchecked(bytes)
150 }
151}
152
153unsafe impl Send for InternedStringContent {}
154unsafe impl Sync for InternedStringContent {}
155
156#[repr(C)]
170pub struct Symbol {
171 content: InternedStringContent,
172}
173
174impl Symbol {
175 pub fn as_str(&self) -> &str {
176 self.content.as_str()
177 }
178}
179
180impl PartialEq<&str> for Symbol {
181 fn eq(&self, other: &&str) -> bool {
182 self.as_str() == *other
183 }
184}
185
186impl StackGraph {
187 pub fn add_symbol<S: AsRef<str> + ?Sized>(&mut self, symbol: &S) -> Handle<Symbol> {
190 let symbol = symbol.as_ref();
191 if let Some(handle) = self.symbol_handles.get(symbol) {
192 return *handle;
193 }
194
195 let interned = self.interned_strings.add(symbol);
196 let hash_key = unsafe { interned.as_hash_key() };
197 let handle = self.symbols.add(Symbol { content: interned });
198 self.symbol_handles.insert(hash_key, handle);
199 handle
200 }
201
202 pub fn iter_symbols(&self) -> impl Iterator<Item = Handle<Symbol>> {
206 self.symbols.iter_handles()
207 }
208}
209
210impl Index<Handle<Symbol>> for StackGraph {
211 type Output = str;
212 #[inline(always)]
213 fn index(&self, handle: Handle<Symbol>) -> &str {
214 self.symbols.get(handle).as_str()
215 }
216}
217
218#[doc(hidden)]
219pub struct DisplaySymbol<'a> {
220 wrapped: Handle<Symbol>,
221 graph: &'a StackGraph,
222}
223
224impl<'a> Display for DisplaySymbol<'a> {
225 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
226 write!(f, "{}", &self.graph[self.wrapped])
227 }
228}
229
230impl Handle<Symbol> {
231 pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
232 DisplaySymbol {
233 wrapped: self,
234 graph,
235 }
236 }
237}
238
239#[repr(C)]
244pub struct InternedString {
245 content: InternedStringContent,
246}
247
248impl InternedString {
249 fn as_str(&self) -> &str {
250 self.content.as_str()
251 }
252}
253
254impl PartialEq<&str> for InternedString {
255 fn eq(&self, other: &&str) -> bool {
256 self.as_str() == *other
257 }
258}
259
260impl StackGraph {
261 pub fn add_string<S: AsRef<str> + ?Sized>(&mut self, string: &S) -> Handle<InternedString> {
264 let string = string.as_ref();
265 if let Some(handle) = self.string_handles.get(string) {
266 return *handle;
267 }
268
269 let interned = self.interned_strings.add(string);
270 let hash_key = unsafe { interned.as_hash_key() };
271 let handle = self.strings.add(InternedString { content: interned });
272 self.string_handles.insert(hash_key, handle);
273 handle
274 }
275
276 pub fn iter_strings(&self) -> impl Iterator<Item = Handle<InternedString>> {
280 self.strings.iter_handles()
281 }
282}
283
284impl Index<Handle<InternedString>> for StackGraph {
285 type Output = str;
286 #[inline(always)]
287 fn index(&self, handle: Handle<InternedString>) -> &str {
288 self.strings.get(handle).as_str()
289 }
290}
291
292#[doc(hidden)]
293pub struct DisplayInternedString<'a> {
294 wrapped: Handle<InternedString>,
295 graph: &'a StackGraph,
296}
297
298impl<'a> Display for DisplayInternedString<'a> {
299 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
300 write!(f, "{}", &self.graph[self.wrapped])
301 }
302}
303
304impl Handle<InternedString> {
305 pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
306 DisplayInternedString {
307 wrapped: self,
308 graph,
309 }
310 }
311}
312
313pub struct File {
324 name: InternedStringContent,
326}
327
328impl File {
329 pub fn name(&self) -> &str {
330 self.name.as_str()
331 }
332}
333
334impl StackGraph {
335 pub fn add_file<S: AsRef<str> + ?Sized>(
340 &mut self,
341 name: &S,
342 ) -> Result<Handle<File>, Handle<File>> {
343 let name = name.as_ref();
344 if let Some(handle) = self.file_handles.get(name) {
345 return Err(*handle);
346 }
347
348 let interned = self.interned_strings.add(name);
349 let hash_key = unsafe { interned.as_hash_key() };
350 let handle = self.files.add(File { name: interned });
351 self.file_handles.insert(hash_key, handle);
352 Ok(handle)
353 }
354
355 #[inline(always)]
359 pub fn get_or_create_file<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> Handle<File> {
360 self.add_file(name).unwrap_or_else(|handle| handle)
361 }
362
363 pub fn get_file<S: AsRef<str> + ?Sized>(&self, name: &S) -> Option<Handle<File>> {
365 let name = name.as_ref();
366 self.file_handles.get(name).copied()
367 }
368}
369
370impl StackGraph {
371 pub fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
374 self.node_id_handles.nodes_for_file(file)
375 }
376
377 pub fn iter_files(&self) -> impl Iterator<Item = Handle<File>> + '_ {
381 self.files.iter_handles()
382 }
383}
384
385impl Display for File {
386 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
387 write!(f, "{}", self.name())
388 }
389}
390
391impl Index<Handle<File>> for StackGraph {
392 type Output = File;
393 #[inline(always)]
394 fn index(&self, handle: Handle<File>) -> &File {
395 &self.files.get(handle)
396 }
397}
398
399#[doc(hidden)]
400pub struct DisplayFile<'a> {
401 wrapped: Handle<File>,
402 graph: &'a StackGraph,
403}
404
405impl<'a> Display for DisplayFile<'a> {
406 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
407 write!(f, "{}", self.graph[self.wrapped])
408 }
409}
410
411impl Handle<File> {
412 pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
413 DisplayFile {
414 wrapped: self,
415 graph,
416 }
417 }
418}
419
420#[repr(C)]
428#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
429pub struct NodeID {
430 file: ControlledOption<Handle<File>>,
431 local_id: u32,
432}
433
434pub(crate) const ROOT_NODE_ID: u32 = 1;
435pub(crate) const JUMP_TO_NODE_ID: u32 = 2;
436
437impl NodeID {
438 #[inline(always)]
440 pub fn root() -> NodeID {
441 NodeID {
442 file: ControlledOption::none(),
443 local_id: ROOT_NODE_ID,
444 }
445 }
446
447 #[inline(always)]
449 pub fn jump_to() -> NodeID {
450 NodeID {
451 file: ControlledOption::none(),
452 local_id: JUMP_TO_NODE_ID,
453 }
454 }
455
456 #[inline(always)]
458 pub fn new_in_file(file: Handle<File>, local_id: u32) -> NodeID {
459 NodeID {
460 file: ControlledOption::some(file),
461 local_id,
462 }
463 }
464
465 #[inline(always)]
467 pub fn is_root(self) -> bool {
468 self.file.is_none() && self.local_id == ROOT_NODE_ID
469 }
470
471 #[inline(always)]
473 pub fn is_jump_to(self) -> bool {
474 self.file.is_none() && self.local_id == JUMP_TO_NODE_ID
475 }
476
477 #[inline(always)]
480 pub fn file(self) -> Option<Handle<File>> {
481 self.file.into()
482 }
483
484 #[inline(always)]
487 pub fn local_id(self) -> u32 {
488 self.local_id
489 }
490
491 #[inline(always)]
494 pub fn is_in_file(self, file: Handle<File>) -> bool {
495 match self.file.into_option() {
496 Some(this_file) => this_file == file,
497 _ => true,
498 }
499 }
500}
501
502#[doc(hidden)]
503pub struct DisplayNodeID<'a> {
504 wrapped: NodeID,
505 graph: &'a StackGraph,
506}
507
508impl<'a> Display for DisplayNodeID<'a> {
509 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
510 match self.wrapped.file.into_option() {
511 Some(file) => write!(f, "{}({})", file.display(self.graph), self.wrapped.local_id),
512 None => {
513 if self.wrapped.is_root() {
514 write!(f, "[root]")
515 } else if self.wrapped.is_jump_to() {
516 write!(f, "[jump]")
517 } else {
518 unreachable!();
519 }
520 }
521 }
522 }
523}
524
525impl NodeID {
526 pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
527 DisplayNodeID {
528 wrapped: self,
529 graph,
530 }
531 }
532}
533
534#[repr(C)]
536pub enum Node {
537 DropScopes(DropScopesNode),
538 JumpTo(JumpToNode),
539 PopScopedSymbol(PopScopedSymbolNode),
540 PopSymbol(PopSymbolNode),
541 PushScopedSymbol(PushScopedSymbolNode),
542 PushSymbol(PushSymbolNode),
543 Root(RootNode),
544 Scope(ScopeNode),
545}
546
547impl Node {
548 #[inline(always)]
549 pub fn is_exported_scope(&self) -> bool {
550 match self {
551 Node::Scope(node) => node.is_exported,
552 _ => false,
553 }
554 }
555
556 #[inline(always)]
557 pub fn is_definition(&self) -> bool {
558 match self {
559 Node::PopScopedSymbol(node) => node.is_definition,
560 Node::PopSymbol(node) => node.is_definition,
561 _ => false,
562 }
563 }
564
565 #[inline(always)]
566 pub fn is_reference(&self) -> bool {
567 match self {
568 Node::PushScopedSymbol(node) => node.is_reference,
569 Node::PushSymbol(node) => node.is_reference,
570 _ => false,
571 }
572 }
573
574 #[inline(always)]
575 pub fn is_jump_to(&self) -> bool {
576 matches!(self, Node::JumpTo(_))
577 }
578
579 #[inline(always)]
580 pub fn is_root(&self) -> bool {
581 matches!(self, Node::Root(_))
582 }
583
584 #[inline(always)]
585 pub fn is_endpoint(&self) -> bool {
586 self.is_definition() || self.is_exported_scope() || self.is_reference() || self.is_root()
587 }
588
589 pub fn symbol(&self) -> Option<Handle<Symbol>> {
592 match self {
593 Node::PushScopedSymbol(node) => Some(node.symbol),
594 Node::PushSymbol(node) => Some(node.symbol),
595 Node::PopScopedSymbol(node) => Some(node.symbol),
596 Node::PopSymbol(node) => Some(node.symbol),
597 _ => None,
598 }
599 }
600
601 pub fn scope(&self) -> Option<NodeID> {
604 match self {
605 Node::PushScopedSymbol(node) => Some(node.scope),
606 _ => None,
607 }
608 }
609
610 pub fn id(&self) -> NodeID {
612 match self {
613 Node::DropScopes(node) => node.id,
614 Node::JumpTo(node) => node.id,
615 Node::PushScopedSymbol(node) => node.id,
616 Node::PushSymbol(node) => node.id,
617 Node::PopScopedSymbol(node) => node.id,
618 Node::PopSymbol(node) => node.id,
619 Node::Root(node) => node.id,
620 Node::Scope(node) => node.id,
621 }
622 }
623
624 #[inline(always)]
627 pub fn file(&self) -> Option<Handle<File>> {
628 self.id().file()
629 }
630
631 #[inline(always)]
634 pub fn is_in_file(&self, file: Handle<File>) -> bool {
635 self.id().is_in_file(file)
636 }
637
638 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
639 DisplayNode {
640 wrapped: self,
641 graph,
642 }
643 }
644}
645
646impl StackGraph {
647 #[inline(always)]
649 pub fn jump_to_node() -> Handle<Node> {
650 Handle::new(unsafe { NonZeroU32::new_unchecked(2) })
651 }
652
653 #[inline(always)]
655 pub fn root_node() -> Handle<Node> {
656 Handle::new(unsafe { NonZeroU32::new_unchecked(1) })
657 }
658
659 pub fn new_node_id(&mut self, file: Handle<File>) -> NodeID {
663 self.node_id_handles.unused_id(file)
664 }
665
666 pub fn iter_nodes(&self) -> impl Iterator<Item = Handle<Node>> {
669 self.nodes.iter_handles()
670 }
671
672 pub fn node_for_id(&self, id: NodeID) -> Option<Handle<Node>> {
674 if id.file().is_some() {
675 self.node_id_handles.try_handle_for_id(id)
676 } else if id.is_root() {
677 Some(StackGraph::root_node())
678 } else if id.is_jump_to() {
679 Some(StackGraph::jump_to_node())
680 } else {
681 None
682 }
683 }
684
685 pub(crate) fn add_node(&mut self, id: NodeID, node: Node) -> Option<Handle<Node>> {
686 if let Some(_) = self.node_id_handles.handle_for_id(id) {
687 return None;
688 }
689 let handle = self.nodes.add(node);
690 self.node_id_handles.set_handle_for_id(id, handle);
691 Some(handle)
692 }
693
694 pub(crate) fn get_or_create_node(&mut self, id: NodeID, node: Node) -> Handle<Node> {
695 if let Some(handle) = self.node_id_handles.handle_for_id(id) {
696 return handle;
697 }
698 let handle = self.nodes.add(node);
699 self.node_id_handles.set_handle_for_id(id, handle);
700 handle
701 }
702}
703
704#[doc(hidden)]
705pub struct DisplayNode<'a> {
706 wrapped: &'a Node,
707 graph: &'a StackGraph,
708}
709
710impl<'a> Display for DisplayNode<'a> {
711 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
712 match self.wrapped {
713 Node::DropScopes(node) => node.display(self.graph).fmt(f),
714 Node::JumpTo(node) => node.fmt(f),
715 Node::PushScopedSymbol(node) => node.display(self.graph).fmt(f),
716 Node::PushSymbol(node) => node.display(self.graph).fmt(f),
717 Node::PopScopedSymbol(node) => node.display(self.graph).fmt(f),
718 Node::PopSymbol(node) => node.display(self.graph).fmt(f),
719 Node::Root(node) => node.fmt(f),
720 Node::Scope(node) => node.display(self.graph).fmt(f),
721 }
722 }
723}
724
725impl Handle<Node> {
726 pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
727 DisplayNode {
728 wrapped: &graph[self],
729 graph,
730 }
731 }
732}
733
734impl Index<Handle<Node>> for StackGraph {
735 type Output = Node;
736 #[inline(always)]
737 fn index(&self, handle: Handle<Node>) -> &Node {
738 self.nodes.get(handle)
739 }
740}
741
742impl IndexMut<Handle<Node>> for StackGraph {
743 #[inline(always)]
744 fn index_mut(&mut self, handle: Handle<Node>) -> &mut Node {
745 self.nodes.get_mut(handle)
746 }
747}
748
749#[repr(C)]
751pub struct DropScopesNode {
752 pub id: NodeID,
754 _symbol: ControlledOption<Handle<Symbol>>,
755 _scope: NodeID,
756 _is_endpoint: bool,
757}
758
759impl From<DropScopesNode> for Node {
760 fn from(node: DropScopesNode) -> Node {
761 Node::DropScopes(node)
762 }
763}
764
765impl StackGraph {
766 pub fn add_drop_scopes_node(&mut self, id: NodeID) -> Option<Handle<Node>> {
768 let node = DropScopesNode {
769 id,
770 _symbol: ControlledOption::none(),
771 _scope: NodeID::default(),
772 _is_endpoint: false,
773 };
774 self.add_node(id, node.into())
775 }
776}
777
778impl DropScopesNode {
779 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
780 DisplayDropScopesNode {
781 wrapped: self,
782 graph,
783 }
784 }
785}
786
787#[doc(hidden)]
788pub struct DisplayDropScopesNode<'a> {
789 wrapped: &'a DropScopesNode,
790 graph: &'a StackGraph,
791}
792
793impl<'a> Display for DisplayDropScopesNode<'a> {
794 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
795 if f.alternate() {
796 write!(f, "[{}]", self.wrapped.id.display(self.graph))
797 } else {
798 write!(f, "[{} drop scopes]", self.wrapped.id.display(self.graph))
799 }
800 }
801}
802
803#[repr(C)]
806pub struct JumpToNode {
807 id: NodeID,
808 _symbol: ControlledOption<Handle<Symbol>>,
809 _scope: NodeID,
810 _is_endpoint: bool,
811}
812
813impl From<JumpToNode> for Node {
814 fn from(node: JumpToNode) -> Node {
815 Node::JumpTo(node)
816 }
817}
818
819impl JumpToNode {
820 fn new() -> JumpToNode {
821 JumpToNode {
822 id: NodeID::jump_to(),
823 _symbol: ControlledOption::none(),
824 _scope: NodeID::default(),
825 _is_endpoint: false,
826 }
827 }
828}
829
830impl Display for JumpToNode {
831 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
832 write!(f, "[jump to scope]")
833 }
834}
835
836#[repr(C)]
840pub struct PopScopedSymbolNode {
841 pub id: NodeID,
843 pub symbol: Handle<Symbol>,
845 _scope: NodeID,
846 pub is_definition: bool,
848}
849
850impl From<PopScopedSymbolNode> for Node {
851 fn from(node: PopScopedSymbolNode) -> Node {
852 Node::PopScopedSymbol(node)
853 }
854}
855
856impl StackGraph {
857 pub fn add_pop_scoped_symbol_node(
859 &mut self,
860 id: NodeID,
861 symbol: Handle<Symbol>,
862 is_definition: bool,
863 ) -> Option<Handle<Node>> {
864 let node = PopScopedSymbolNode {
865 id,
866 symbol,
867 _scope: NodeID::default(),
868 is_definition,
869 };
870 self.add_node(id, node.into())
871 }
872}
873
874impl PopScopedSymbolNode {
875 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
876 DisplayPopScopedSymbolNode {
877 wrapped: self,
878 graph,
879 }
880 }
881}
882
883#[doc(hidden)]
884pub struct DisplayPopScopedSymbolNode<'a> {
885 wrapped: &'a PopScopedSymbolNode,
886 graph: &'a StackGraph,
887}
888
889impl<'a> Display for DisplayPopScopedSymbolNode<'a> {
890 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
891 if f.alternate() {
892 write!(f, "[{}]", self.wrapped.id.display(self.graph))
893 } else {
894 write!(
895 f,
896 "[{} {} {}]",
897 self.wrapped.id.display(self.graph),
898 if self.wrapped.is_definition {
899 "scoped definition"
900 } else {
901 "pop scoped"
902 },
903 self.wrapped.symbol.display(self.graph),
904 )
905 }
906 }
907}
908
909#[repr(C)]
912pub struct PopSymbolNode {
913 pub id: NodeID,
915 pub symbol: Handle<Symbol>,
917 _scope: NodeID,
918 pub is_definition: bool,
920}
921
922impl From<PopSymbolNode> for Node {
923 fn from(node: PopSymbolNode) -> Node {
924 Node::PopSymbol(node)
925 }
926}
927
928impl StackGraph {
929 pub fn add_pop_symbol_node(
931 &mut self,
932 id: NodeID,
933 symbol: Handle<Symbol>,
934 is_definition: bool,
935 ) -> Option<Handle<Node>> {
936 let node = PopSymbolNode {
937 id,
938 symbol,
939 _scope: NodeID::default(),
940 is_definition,
941 };
942 self.add_node(id, node.into())
943 }
944}
945
946impl PopSymbolNode {
947 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
948 DisplayPopSymbolNode {
949 wrapped: self,
950 graph,
951 }
952 }
953}
954
955#[doc(hidden)]
956pub struct DisplayPopSymbolNode<'a> {
957 wrapped: &'a PopSymbolNode,
958 graph: &'a StackGraph,
959}
960
961impl<'a> Display for DisplayPopSymbolNode<'a> {
962 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
963 if f.alternate() {
964 write!(f, "[{}]", self.wrapped.id.display(self.graph))
965 } else {
966 write!(
967 f,
968 "[{} {} {}]",
969 self.wrapped.id.display(self.graph),
970 if self.wrapped.is_definition {
971 "definition"
972 } else {
973 "pop"
974 },
975 self.wrapped.symbol.display(self.graph),
976 )
977 }
978 }
979}
980
981#[repr(C)]
983pub struct PushScopedSymbolNode {
984 pub id: NodeID,
986 pub symbol: Handle<Symbol>,
988 pub scope: NodeID,
991 pub is_reference: bool,
993 _phantom: (),
994}
995
996impl From<PushScopedSymbolNode> for Node {
997 fn from(node: PushScopedSymbolNode) -> Node {
998 Node::PushScopedSymbol(node)
999 }
1000}
1001
1002impl StackGraph {
1003 pub fn add_push_scoped_symbol_node(
1005 &mut self,
1006 id: NodeID,
1007 symbol: Handle<Symbol>,
1008 scope: NodeID,
1009 is_reference: bool,
1010 ) -> Option<Handle<Node>> {
1011 let node = PushScopedSymbolNode {
1012 id,
1013 symbol,
1014 scope,
1015 is_reference,
1016 _phantom: (),
1017 };
1018 self.add_node(id, node.into())
1019 }
1020}
1021
1022impl PushScopedSymbolNode {
1023 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
1024 DisplayPushScopedSymbolNode {
1025 wrapped: self,
1026 graph,
1027 }
1028 }
1029}
1030
1031#[doc(hidden)]
1032pub struct DisplayPushScopedSymbolNode<'a> {
1033 wrapped: &'a PushScopedSymbolNode,
1034 graph: &'a StackGraph,
1035}
1036
1037impl<'a> Display for DisplayPushScopedSymbolNode<'a> {
1038 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1039 if f.alternate() {
1040 write!(f, "[{}]", self.wrapped.id.display(self.graph))
1041 } else {
1042 write!(
1043 f,
1044 "[{} {} {} {}]",
1045 self.wrapped.id.display(self.graph),
1046 if self.wrapped.is_reference {
1047 "scoped reference"
1048 } else {
1049 "push scoped"
1050 },
1051 self.wrapped.symbol.display(self.graph),
1052 self.wrapped.scope.display(self.graph),
1053 )
1054 }
1055 }
1056}
1057
1058#[repr(C)]
1060pub struct PushSymbolNode {
1061 pub id: NodeID,
1063 pub symbol: Handle<Symbol>,
1065 _scope: NodeID,
1066 pub is_reference: bool,
1068}
1069
1070impl From<PushSymbolNode> for Node {
1071 fn from(node: PushSymbolNode) -> Node {
1072 Node::PushSymbol(node)
1073 }
1074}
1075
1076impl StackGraph {
1077 pub fn add_push_symbol_node(
1079 &mut self,
1080 id: NodeID,
1081 symbol: Handle<Symbol>,
1082 is_reference: bool,
1083 ) -> Option<Handle<Node>> {
1084 let node = PushSymbolNode {
1085 id,
1086 symbol,
1087 _scope: NodeID::default(),
1088 is_reference,
1089 };
1090 self.add_node(id, node.into())
1091 }
1092}
1093
1094impl PushSymbolNode {
1095 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
1096 DisplayPushSymbolNode {
1097 wrapped: self,
1098 graph,
1099 }
1100 }
1101}
1102
1103#[doc(hidden)]
1104pub struct DisplayPushSymbolNode<'a> {
1105 wrapped: &'a PushSymbolNode,
1106 graph: &'a StackGraph,
1107}
1108
1109impl<'a> Display for DisplayPushSymbolNode<'a> {
1110 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1111 if f.alternate() {
1112 write!(f, "[{}]", self.wrapped.id.display(self.graph))
1113 } else {
1114 write!(
1115 f,
1116 "[{} {} {}]",
1117 self.wrapped.id.display(self.graph),
1118 if self.wrapped.is_reference {
1119 "reference"
1120 } else {
1121 "push"
1122 },
1123 self.wrapped.symbol.display(self.graph),
1124 )
1125 }
1126 }
1127}
1128
1129#[repr(C)]
1131pub struct RootNode {
1132 id: NodeID,
1133 _symbol: ControlledOption<Handle<Symbol>>,
1134 _scope: NodeID,
1135 _is_endpoint: bool,
1136}
1137
1138impl From<RootNode> for Node {
1139 fn from(node: RootNode) -> Node {
1140 Node::Root(node)
1141 }
1142}
1143
1144impl RootNode {
1145 fn new() -> RootNode {
1146 RootNode {
1147 id: NodeID::root(),
1148 _symbol: ControlledOption::none(),
1149 _scope: NodeID::default(),
1150 _is_endpoint: false,
1151 }
1152 }
1153}
1154
1155impl Display for RootNode {
1156 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1157 write!(f, "[root]")
1158 }
1159}
1160
1161struct NodeIDHandles {
1162 files: SupplementalArena<File, Vec<Option<Handle<Node>>>>,
1163}
1164
1165impl NodeIDHandles {
1166 fn new() -> NodeIDHandles {
1167 NodeIDHandles {
1168 files: SupplementalArena::new(),
1169 }
1170 }
1171
1172 fn try_handle_for_id(&self, node_id: NodeID) -> Option<Handle<Node>> {
1173 let file_entry = self.files.get(node_id.file().unwrap())?;
1174 let node_index = node_id.local_id as usize;
1175 if node_index >= file_entry.len() {
1176 return None;
1177 }
1178 file_entry[node_index]
1179 }
1180
1181 fn handle_for_id(&mut self, node_id: NodeID) -> Option<Handle<Node>> {
1182 let file_entry = &mut self.files[node_id.file().unwrap()];
1183 let node_index = node_id.local_id as usize;
1184 if node_index >= file_entry.len() {
1185 file_entry.resize(node_index + 1, None);
1186 }
1187 file_entry[node_index]
1188 }
1189
1190 fn set_handle_for_id(&mut self, node_id: NodeID, handle: Handle<Node>) {
1191 let file_entry = &mut self.files[node_id.file().unwrap()];
1192 let node_index = node_id.local_id as usize;
1193 file_entry[node_index] = Some(handle);
1194 }
1195
1196 fn unused_id(&mut self, file: Handle<File>) -> NodeID {
1197 let local_id = self
1198 .files
1199 .get(file)
1200 .map(|file_entry| file_entry.len() as u32)
1201 .unwrap_or(0);
1202 NodeID::new_in_file(file, local_id)
1203 }
1204
1205 fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
1206 let file_entry = match self.files.get(file) {
1207 Some(file_entry) => file_entry,
1208 None => return Either::Left(std::iter::empty()),
1209 };
1210 Either::Right(file_entry.iter().filter_map(|entry| *entry))
1211 }
1212}
1213
1214#[repr(C)]
1218pub struct ScopeNode {
1219 pub id: NodeID,
1221 _symbol: ControlledOption<Handle<Symbol>>,
1222 _scope: NodeID,
1223 pub is_exported: bool,
1224}
1225
1226impl From<ScopeNode> for Node {
1227 fn from(node: ScopeNode) -> Node {
1228 Node::Scope(node)
1229 }
1230}
1231
1232impl StackGraph {
1233 pub fn add_scope_node(&mut self, id: NodeID, is_exported: bool) -> Option<Handle<Node>> {
1235 let node = ScopeNode {
1236 id,
1237 _symbol: ControlledOption::none(),
1238 _scope: NodeID::default(),
1239 is_exported,
1240 };
1241 self.add_node(id, node.into())
1242 }
1243}
1244
1245impl ScopeNode {
1246 pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
1247 DisplayScopeNode {
1248 wrapped: self,
1249 graph,
1250 }
1251 }
1252}
1253
1254#[doc(hidden)]
1255pub struct DisplayScopeNode<'a> {
1256 wrapped: &'a ScopeNode,
1257 graph: &'a StackGraph,
1258}
1259
1260impl<'a> Display for DisplayScopeNode<'a> {
1261 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1262 if f.alternate() {
1263 write!(f, "[{}]", self.wrapped.id.display(self.graph))
1264 } else {
1265 write!(
1266 f,
1267 "[{}{} scope]",
1268 self.wrapped.id.display(self.graph),
1269 if self.wrapped.is_exported {
1270 " exported"
1271 } else {
1272 ""
1273 },
1274 )
1275 }
1276 }
1277}
1278
1279#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1289pub struct Edge {
1290 pub source: Handle<Node>,
1291 pub sink: Handle<Node>,
1292 pub precedence: i32,
1293}
1294
1295pub(crate) struct OutgoingEdge {
1296 sink: Handle<Node>,
1297 precedence: i32,
1298}
1299
1300impl StackGraph {
1301 pub fn add_edge(&mut self, source: Handle<Node>, sink: Handle<Node>, precedence: i32) {
1303 let edges = &mut self.outgoing_edges[source];
1304 if let Err(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
1305 edges.insert(index, OutgoingEdge { sink, precedence });
1306 self.incoming_edges[sink] += Degree::One;
1307 }
1308 }
1309
1310 pub fn set_edge_precedence(
1312 &mut self,
1313 source: Handle<Node>,
1314 sink: Handle<Node>,
1315 precedence: i32,
1316 ) {
1317 let edges = &mut self.outgoing_edges[source];
1318 if let Ok(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
1319 edges[index].precedence = precedence;
1320 }
1321 }
1322
1323 pub fn outgoing_edges(&self, source: Handle<Node>) -> impl Iterator<Item = Edge> + '_ {
1325 match self.outgoing_edges.get(source) {
1326 Some(edges) => Either::Right(edges.iter().map(move |o| Edge {
1327 source,
1328 sink: o.sink,
1329 precedence: o.precedence,
1330 })),
1331 None => Either::Left(std::iter::empty()),
1332 }
1333 }
1334
1335 pub fn incoming_edge_degree(&self, sink: Handle<Node>) -> Degree {
1337 self.incoming_edges
1338 .get(sink)
1339 .cloned()
1340 .unwrap_or(Degree::Zero)
1341 }
1342}
1343
1344#[repr(C)]
1349#[derive(Default)]
1350pub struct SourceInfo {
1351 pub span: lsp_positions::Span,
1353 pub syntax_type: ControlledOption<Handle<InternedString>>,
1355 pub containing_line: ControlledOption<Handle<InternedString>>,
1357 pub definiens_span: lsp_positions::Span,
1362 pub fully_qualified_name: ControlledOption<Handle<InternedString>>,
1365}
1366
1367impl StackGraph {
1368 pub fn source_info(&self, node: Handle<Node>) -> Option<&SourceInfo> {
1370 self.source_info.get(node)
1371 }
1372
1373 pub fn source_info_mut(&mut self, node: Handle<Node>) -> &mut SourceInfo {
1376 &mut self.source_info[node]
1377 }
1378}
1379
1380#[derive(Default)]
1385pub struct DebugInfo {
1386 entries: Vec<DebugEntry>,
1387}
1388
1389impl DebugInfo {
1390 pub fn add(&mut self, key: Handle<InternedString>, value: Handle<InternedString>) {
1391 self.entries.push(DebugEntry { key, value });
1392 }
1393
1394 pub fn iter(&self) -> std::slice::Iter<DebugEntry> {
1395 self.entries.iter()
1396 }
1397}
1398
1399pub struct DebugEntry {
1401 pub key: Handle<InternedString>,
1402 pub value: Handle<InternedString>,
1403}
1404
1405impl StackGraph {
1406 pub fn node_debug_info(&self, node: Handle<Node>) -> Option<&DebugInfo> {
1408 self.node_debug_info.get(node)
1409 }
1410
1411 pub fn node_debug_info_mut(&mut self, node: Handle<Node>) -> &mut DebugInfo {
1413 &mut self.node_debug_info[node]
1414 }
1415
1416 pub fn edge_debug_info(&self, source: Handle<Node>, sink: Handle<Node>) -> Option<&DebugInfo> {
1418 self.edge_debug_info.get(source).and_then(|es| {
1419 match es.binary_search_by_key(&sink, |e| e.0) {
1420 Ok(idx) => Some(&es[idx].1),
1421 Err(_) => None,
1422 }
1423 })
1424 }
1425
1426 pub fn edge_debug_info_mut(
1428 &mut self,
1429 source: Handle<Node>,
1430 sink: Handle<Node>,
1431 ) -> &mut DebugInfo {
1432 let es = &mut self.edge_debug_info[source];
1433 let idx = match es.binary_search_by_key(&sink, |e| e.0) {
1434 Ok(idx) => idx,
1435 Err(idx) => {
1436 es.insert(idx, (sink, DebugInfo::default()));
1437 idx
1438 }
1439 };
1440 &mut es[idx].1
1441 }
1442}
1443
1444#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1448#[repr(u8)]
1449pub enum Degree {
1450 Zero,
1451 One,
1452 Multiple,
1453}
1454
1455impl Default for Degree {
1456 fn default() -> Self {
1457 Self::Zero
1458 }
1459}
1460
1461impl std::ops::Add for Degree {
1462 type Output = Self;
1463 fn add(self, rhs: Self) -> Self::Output {
1464 match (self, rhs) {
1465 (Self::Zero, result) | (result, Self::Zero) => result,
1466 _ => Self::Multiple,
1467 }
1468 }
1469}
1470
1471impl std::ops::AddAssign for Degree {
1472 fn add_assign(&mut self, rhs: Self) {
1473 *self = *self + rhs;
1474 }
1475}
1476
1477pub struct StackGraph {
1479 interned_strings: InternedStringArena,
1480 pub(crate) symbols: Arena<Symbol>,
1481 symbol_handles: FxHashMap<&'static str, Handle<Symbol>>,
1482 pub(crate) strings: Arena<InternedString>,
1483 string_handles: FxHashMap<&'static str, Handle<InternedString>>,
1484 pub(crate) files: Arena<File>,
1485 file_handles: FxHashMap<&'static str, Handle<File>>,
1486 pub(crate) nodes: Arena<Node>,
1487 pub(crate) source_info: SupplementalArena<Node, SourceInfo>,
1488 node_id_handles: NodeIDHandles,
1489 outgoing_edges: SupplementalArena<Node, SmallVec<[OutgoingEdge; 4]>>,
1490 incoming_edges: SupplementalArena<Node, Degree>,
1491 pub(crate) node_debug_info: SupplementalArena<Node, DebugInfo>,
1492 pub(crate) edge_debug_info: SupplementalArena<Node, SmallVec<[(Handle<Node>, DebugInfo); 4]>>,
1493}
1494
1495impl StackGraph {
1496 pub fn new() -> StackGraph {
1498 StackGraph::default()
1499 }
1500
1501 pub fn add_from_graph(
1504 &mut self,
1505 other: &StackGraph,
1506 ) -> Result<Vec<Handle<File>>, Handle<File>> {
1507 let mut files = HashMap::new();
1508 for other_file in other.iter_files() {
1509 let file = self.add_file(other[other_file].name())?;
1510 files.insert(other_file, file);
1511 }
1512 let files = files;
1513 let node_id = |other_node_id: NodeID| {
1514 if other_node_id.is_root() {
1515 NodeID::root()
1516 } else if other_node_id.is_jump_to() {
1517 NodeID::jump_to()
1518 } else {
1519 NodeID::new_in_file(
1520 files[&other_node_id.file.into_option().unwrap()],
1521 other_node_id.local_id,
1522 )
1523 }
1524 };
1525 let mut nodes = HashMap::new();
1526 nodes.insert(Self::root_node(), Self::root_node());
1527 nodes.insert(Self::jump_to_node(), Self::jump_to_node());
1528 for other_file in files.keys().cloned() {
1529 let file = files[&other_file];
1530 for other_node in other.nodes_for_file(other_file) {
1531 let value: Node = match other[other_node] {
1532 Node::DropScopes(DropScopesNode { id, .. }) => DropScopesNode {
1533 id: NodeID::new_in_file(file, id.local_id),
1534 _symbol: ControlledOption::default(),
1535 _scope: NodeID::default(),
1536 _is_endpoint: bool::default(),
1537 }
1538 .into(),
1539 Node::JumpTo(JumpToNode { .. }) => JumpToNode {
1540 id: NodeID::jump_to(),
1541 _symbol: ControlledOption::default(),
1542 _scope: NodeID::default(),
1543 _is_endpoint: bool::default(),
1544 }
1545 .into(),
1546 Node::PopScopedSymbol(PopScopedSymbolNode {
1547 id,
1548 symbol,
1549 is_definition,
1550 ..
1551 }) => PopScopedSymbolNode {
1552 id: NodeID::new_in_file(file, id.local_id),
1553 symbol: self.add_symbol(&other[symbol]),
1554 _scope: NodeID::default(),
1555 is_definition: is_definition,
1556 }
1557 .into(),
1558 Node::PopSymbol(PopSymbolNode {
1559 id,
1560 symbol,
1561 is_definition,
1562 ..
1563 }) => PopSymbolNode {
1564 id: NodeID::new_in_file(file, id.local_id),
1565 symbol: self.add_symbol(&other[symbol]),
1566 _scope: NodeID::default(),
1567 is_definition: is_definition,
1568 }
1569 .into(),
1570 Node::PushScopedSymbol(PushScopedSymbolNode {
1571 id,
1572 symbol,
1573 scope,
1574 is_reference,
1575 ..
1576 }) => PushScopedSymbolNode {
1577 id: NodeID::new_in_file(file, id.local_id),
1578 symbol: self.add_symbol(&other[symbol]),
1579 scope: node_id(scope),
1580 is_reference: is_reference,
1581 _phantom: (),
1582 }
1583 .into(),
1584 Node::PushSymbol(PushSymbolNode {
1585 id,
1586 symbol,
1587 is_reference,
1588 ..
1589 }) => PushSymbolNode {
1590 id: NodeID::new_in_file(file, id.local_id),
1591 symbol: self.add_symbol(&other[symbol]),
1592 _scope: NodeID::default(),
1593 is_reference: is_reference,
1594 }
1595 .into(),
1596 Node::Root(RootNode { .. }) => RootNode {
1597 id: NodeID::root(),
1598 _symbol: ControlledOption::default(),
1599 _scope: NodeID::default(),
1600 _is_endpoint: bool::default(),
1601 }
1602 .into(),
1603 Node::Scope(ScopeNode {
1604 id, is_exported, ..
1605 }) => ScopeNode {
1606 id: NodeID::new_in_file(file, id.local_id),
1607 _symbol: ControlledOption::default(),
1608 _scope: NodeID::default(),
1609 is_exported: is_exported,
1610 }
1611 .into(),
1612 };
1613 let node = self.add_node(value.id(), value).unwrap();
1614 nodes.insert(other_node, node);
1615 if let Some(source_info) = other.source_info(other_node) {
1616 *self.source_info_mut(node) = SourceInfo {
1617 span: source_info.span.clone(),
1618 syntax_type: source_info
1619 .syntax_type
1620 .into_option()
1621 .map(|st| self.add_string(&other[st]))
1622 .into(),
1623 containing_line: source_info
1624 .containing_line
1625 .into_option()
1626 .map(|cl| self.add_string(&other[cl]))
1627 .into(),
1628 definiens_span: source_info.definiens_span.clone(),
1629 fully_qualified_name: ControlledOption::default(),
1630 };
1631 }
1632 if let Some(debug_info) = other.node_debug_info(other_node) {
1633 *self.node_debug_info_mut(node) = DebugInfo {
1634 entries: debug_info
1635 .entries
1636 .iter()
1637 .map(|e| DebugEntry {
1638 key: self.add_string(&other[e.key]),
1639 value: self.add_string(&other[e.value]),
1640 })
1641 .collect::<Vec<_>>(),
1642 };
1643 }
1644 }
1645 for other_node in nodes.keys().cloned() {
1646 for other_edge in other.outgoing_edges(other_node) {
1647 self.add_edge(
1648 nodes[&other_edge.source],
1649 nodes[&other_edge.sink],
1650 other_edge.precedence,
1651 );
1652 }
1653 }
1654 }
1655 Ok(files.into_values().collect())
1656 }
1657}
1658
1659impl Default for StackGraph {
1660 fn default() -> StackGraph {
1661 let mut nodes = Arena::new();
1662 nodes.add(RootNode::new().into());
1663 nodes.add(JumpToNode::new().into());
1664
1665 StackGraph {
1666 interned_strings: InternedStringArena::new(),
1667 symbols: Arena::new(),
1668 symbol_handles: FxHashMap::default(),
1669 strings: Arena::new(),
1670 string_handles: FxHashMap::default(),
1671 files: Arena::new(),
1672 file_handles: FxHashMap::default(),
1673 nodes,
1674 source_info: SupplementalArena::new(),
1675 node_id_handles: NodeIDHandles::new(),
1676 outgoing_edges: SupplementalArena::new(),
1677 incoming_edges: SupplementalArena::new(),
1678 node_debug_info: SupplementalArena::new(),
1679 edge_debug_info: SupplementalArena::new(),
1680 }
1681 }
1682}