blob: 12d49e4f4b76bdf241a19dc5af1291bea36dd627 [file] [log] [blame]
Rosica Dejanovska8575a842022-09-01 02:05:30 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "rs_bindings_from_cc/collect_namespaces.h"
6
Dmitri Gribenko785831e2023-07-14 06:47:36 -07007#include <string>
8#include <utility>
Rosica Dejanovska8575a842022-09-01 02:05:30 -07009#include <vector>
10
11#include "absl/container/btree_map.h"
12#include "absl/container/flat_hash_map.h"
13#include "absl/strings/string_view.h"
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070014#include "rs_bindings_from_cc/bazel_types.h"
Rosica Dejanovska8575a842022-09-01 02:05:30 -070015#include "rs_bindings_from_cc/ir.h"
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070016#include "llvm/Support/JSON.h"
Rosica Dejanovska8575a842022-09-01 02:05:30 -070017
18namespace crubit {
19namespace {
20
21// A Trie that stores the namespace hierarchy.
22//
23// Consider the following namespace structure:
24// namespace top_level_one {
25// namespace internal {}
26// } // namespace A
27//
28// namespace top_level_two {
29// namespace internal {}
30// }
31//
32// namespace top_level_one {
33// namespace inner {}
34// }
35//
36// A Namespace trie allows us to convert the above to:
37//
38// top_level_one -> [internal, inner]
39// top_level_two -> [internal]
40//
41// Which we can then serialize to JSON.
42class NamespaceTrie {
43 private:
44 struct Node {
45 absl::string_view name;
46 // A map of child namespace name to the index of the namespace node in the
47 // trie_nodes_ vector. We use a btree_map so that at conversion time we get
48 // deterministic JSON output.
49 absl::btree_map<absl::string_view, int> child_name_to_idx;
50 };
51
52 std::vector<Node> trie_nodes_;
53 // A map of top level namespace name to the index of the namespace node in the
54 // trie_nodes_ vector. We use a btree_map so that at conversion time we get
55 // deterministic JSON output.
56 absl::btree_map<absl::string_view, int> top_level_name_to_idx_;
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070057 // The current target's label.
58 BazelLabel label_;
Rosica Dejanovska8575a842022-09-01 02:05:30 -070059 // A map of item id to the IR Namespace item. It allows us to look up the
60 // children namespace items.
61 absl::flat_hash_map<ItemId, const Namespace*>& id_to_namespace_;
62
63 // Creates a node from a Namespace and inserts it into the trie.
64 void InsertNode(int parent_idx, const Namespace* ns) {
65 auto name = ns->name.Ident();
66 auto parent = &trie_nodes_[parent_idx];
67 int child_idx;
68 if (parent->child_name_to_idx.find(name) ==
69 parent->child_name_to_idx.end()) {
70 child_idx = trie_nodes_.size();
71 parent->child_name_to_idx.insert({name, child_idx});
72 // The following line potentially invalidates the "parent" pointer.
73 trie_nodes_.push_back({name, {}});
74 } else {
75 child_idx = parent->child_name_to_idx[name];
76 }
77
78 for (auto ns_child_id : ns->child_item_ids) {
79 if (id_to_namespace_.find(ns_child_id) == id_to_namespace_.end()) {
80 continue;
81 }
82 auto ns_child = id_to_namespace_[ns_child_id];
83 InsertNode(child_idx, ns_child);
84 }
85 }
86
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -070087 // Converts a trie node into the JSON serializable NamespaceNode.
88 NamespaceNode NodeToNamespaceNode(const Node* node) const {
89 std::vector<NamespaceNode> namespaces;
Rosica Dejanovska8575a842022-09-01 02:05:30 -070090 namespaces.reserve(node->child_name_to_idx.size());
91 for (const auto& [_, idx] : node->child_name_to_idx) {
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -070092 namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx]));
Rosica Dejanovska8575a842022-09-01 02:05:30 -070093 }
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -070094 return NamespaceNode{std::string(node->name), std::move(namespaces)};
Rosica Dejanovska8575a842022-09-01 02:05:30 -070095 }
96
97 public:
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070098 NamespaceTrie(BazelLabel label,
99 absl::flat_hash_map<ItemId, const Namespace*>& id_to_namespace)
100 : label_(label), id_to_namespace_(id_to_namespace) {}
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700101
102 NamespaceTrie(NamespaceTrie&) = delete;
103 NamespaceTrie& operator=(NamespaceTrie&) = delete;
104
105 // Creates a trie node from the top level namespace and inserts it into the
106 // trie.
107 void InsertTopLevel(const Namespace* ns) {
108 auto name = ns->name.Ident();
109 int node_idx;
110 if (top_level_name_to_idx_.find(name) == top_level_name_to_idx_.end()) {
111 node_idx = trie_nodes_.size();
112 top_level_name_to_idx_.insert({name, node_idx});
113 trie_nodes_.push_back({name, {}});
114 } else {
115 node_idx = top_level_name_to_idx_[name];
116 }
117
118 for (auto ns_child_id : ns->child_item_ids) {
119 if (id_to_namespace_.find(ns_child_id) == id_to_namespace_.end()) {
120 continue;
121 }
122 auto ns_child = id_to_namespace_[ns_child_id];
123 InsertNode(node_idx, ns_child);
124 }
125 }
126
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700127 // Converts the trie into the JSON serializable NamespacesHierarchy.
128 NamespacesHierarchy ToNamespacesHierarchy() {
129 std::vector<NamespaceNode> namespaces;
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700130 namespaces.reserve(this->top_level_name_to_idx_.size());
131 for (auto& [_, idx] : this->top_level_name_to_idx_) {
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700132 namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx]));
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700133 }
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700134 return NamespacesHierarchy{label_, std::move(namespaces)};
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700135 }
136};
137
138} // namespace
139
140// Returns the current target's namespace hierarchy in JSON serializable format.
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700141NamespacesHierarchy CollectNamespaces(const IR& ir) {
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700142 auto all_namespaces = ir.get_items_if<Namespace>();
143 absl::flat_hash_map<ItemId, const Namespace*> id_to_namespace;
144 for (auto ns : all_namespaces) {
145 // We are not interested in namespaces from different targets.
146 if (ns->owning_target != ir.current_target) {
147 continue;
148 }
149 id_to_namespace.insert({ns->id, ns});
150 }
151
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700152 NamespaceTrie trie(ir.current_target, id_to_namespace);
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700153 for (auto namespace_id : ir.top_level_item_ids) {
154 if (id_to_namespace.count(namespace_id) == 0) {
155 continue;
156 }
157 auto ns = id_to_namespace[namespace_id];
158 trie.InsertTopLevel(ns);
159 }
160
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700161 return trie.ToNamespacesHierarchy();
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700162}
163
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700164llvm::json::Value NamespaceNode::ToJson() const {
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700165 std::vector<llvm::json::Value> json_children;
166 json_children.reserve(children.size());
167 for (const auto& child : children) {
168 json_children.push_back(child.ToJson());
169 }
170
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700171 return llvm::json::Object{
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700172 {"name", name},
173 {"children", std::move(json_children)},
174 };
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700175}
176
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700177llvm::json::Value NamespacesHierarchy::ToJson() const {
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700178 std::vector<llvm::json::Value> json_namespaces;
179 json_namespaces.reserve(namespaces.size());
180 for (const auto& ns : namespaces) {
181 json_namespaces.push_back(ns.ToJson());
182 }
183
184 return llvm::json::Object{
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700185 {"label", label.value()},
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700186 {"namespaces", std::move(json_namespaces)},
187 };
188}
189
190} // namespace crubit