blob: 55ebea892ae0d6645f36a3f231cef28a745d1b96 [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
7#include <string_view>
8#include <vector>
9
10#include "absl/container/btree_map.h"
11#include "absl/container/flat_hash_map.h"
12#include "absl/strings/string_view.h"
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070013#include "rs_bindings_from_cc/bazel_types.h"
Rosica Dejanovska8575a842022-09-01 02:05:30 -070014#include "rs_bindings_from_cc/ir.h"
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070015#include "llvm/Support/JSON.h"
Rosica Dejanovska8575a842022-09-01 02:05:30 -070016
17namespace crubit {
18namespace {
19
20// A Trie that stores the namespace hierarchy.
21//
22// Consider the following namespace structure:
23// namespace top_level_one {
24// namespace internal {}
25// } // namespace A
26//
27// namespace top_level_two {
28// namespace internal {}
29// }
30//
31// namespace top_level_one {
32// namespace inner {}
33// }
34//
35// A Namespace trie allows us to convert the above to:
36//
37// top_level_one -> [internal, inner]
38// top_level_two -> [internal]
39//
40// Which we can then serialize to JSON.
41class NamespaceTrie {
42 private:
43 struct Node {
44 absl::string_view name;
45 // A map of child namespace name to the index of the namespace node in the
46 // trie_nodes_ vector. We use a btree_map so that at conversion time we get
47 // deterministic JSON output.
48 absl::btree_map<absl::string_view, int> child_name_to_idx;
49 };
50
51 std::vector<Node> trie_nodes_;
52 // A map of top level namespace name to the index of the namespace node in the
53 // trie_nodes_ vector. We use a btree_map so that at conversion time we get
54 // deterministic JSON output.
55 absl::btree_map<absl::string_view, int> top_level_name_to_idx_;
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070056 // The current target's label.
57 BazelLabel label_;
Rosica Dejanovska8575a842022-09-01 02:05:30 -070058 // A map of item id to the IR Namespace item. It allows us to look up the
59 // children namespace items.
60 absl::flat_hash_map<ItemId, const Namespace*>& id_to_namespace_;
61
62 // Creates a node from a Namespace and inserts it into the trie.
63 void InsertNode(int parent_idx, const Namespace* ns) {
64 auto name = ns->name.Ident();
65 auto parent = &trie_nodes_[parent_idx];
66 int child_idx;
67 if (parent->child_name_to_idx.find(name) ==
68 parent->child_name_to_idx.end()) {
69 child_idx = trie_nodes_.size();
70 parent->child_name_to_idx.insert({name, child_idx});
71 // The following line potentially invalidates the "parent" pointer.
72 trie_nodes_.push_back({name, {}});
73 } else {
74 child_idx = parent->child_name_to_idx[name];
75 }
76
77 for (auto ns_child_id : ns->child_item_ids) {
78 if (id_to_namespace_.find(ns_child_id) == id_to_namespace_.end()) {
79 continue;
80 }
81 auto ns_child = id_to_namespace_[ns_child_id];
82 InsertNode(child_idx, ns_child);
83 }
84 }
85
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -070086 // Converts a trie node into the JSON serializable NamespaceNode.
87 NamespaceNode NodeToNamespaceNode(const Node* node) const {
88 std::vector<NamespaceNode> namespaces;
Rosica Dejanovska8575a842022-09-01 02:05:30 -070089 namespaces.reserve(node->child_name_to_idx.size());
90 for (const auto& [_, idx] : node->child_name_to_idx) {
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -070091 namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx]));
Rosica Dejanovska8575a842022-09-01 02:05:30 -070092 }
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -070093 return NamespaceNode{std::string(node->name), std::move(namespaces)};
Rosica Dejanovska8575a842022-09-01 02:05:30 -070094 }
95
96 public:
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -070097 NamespaceTrie(BazelLabel label,
98 absl::flat_hash_map<ItemId, const Namespace*>& id_to_namespace)
99 : label_(label), id_to_namespace_(id_to_namespace) {}
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700100
101 NamespaceTrie(NamespaceTrie&) = delete;
102 NamespaceTrie& operator=(NamespaceTrie&) = delete;
103
104 // Creates a trie node from the top level namespace and inserts it into the
105 // trie.
106 void InsertTopLevel(const Namespace* ns) {
107 auto name = ns->name.Ident();
108 int node_idx;
109 if (top_level_name_to_idx_.find(name) == top_level_name_to_idx_.end()) {
110 node_idx = trie_nodes_.size();
111 top_level_name_to_idx_.insert({name, node_idx});
112 trie_nodes_.push_back({name, {}});
113 } else {
114 node_idx = top_level_name_to_idx_[name];
115 }
116
117 for (auto ns_child_id : ns->child_item_ids) {
118 if (id_to_namespace_.find(ns_child_id) == id_to_namespace_.end()) {
119 continue;
120 }
121 auto ns_child = id_to_namespace_[ns_child_id];
122 InsertNode(node_idx, ns_child);
123 }
124 }
125
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700126 // Converts the trie into the JSON serializable NamespacesHierarchy.
127 NamespacesHierarchy ToNamespacesHierarchy() {
128 std::vector<NamespaceNode> namespaces;
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700129 namespaces.reserve(this->top_level_name_to_idx_.size());
130 for (auto& [_, idx] : this->top_level_name_to_idx_) {
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700131 namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx]));
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700132 }
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700133 return NamespacesHierarchy{label_, std::move(namespaces)};
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700134 }
135};
136
137} // namespace
138
139// Returns the current target's namespace hierarchy in JSON serializable format.
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700140NamespacesHierarchy CollectNamespaces(const IR& ir) {
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700141 auto all_namespaces = ir.get_items_if<Namespace>();
142 absl::flat_hash_map<ItemId, const Namespace*> id_to_namespace;
143 for (auto ns : all_namespaces) {
144 // We are not interested in namespaces from different targets.
145 if (ns->owning_target != ir.current_target) {
146 continue;
147 }
148 id_to_namespace.insert({ns->id, ns});
149 }
150
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700151 NamespaceTrie trie(ir.current_target, id_to_namespace);
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700152 for (auto namespace_id : ir.top_level_item_ids) {
153 if (id_to_namespace.count(namespace_id) == 0) {
154 continue;
155 }
156 auto ns = id_to_namespace[namespace_id];
157 trie.InsertTopLevel(ns);
158 }
159
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700160 return trie.ToNamespacesHierarchy();
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700161}
162
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700163llvm::json::Value NamespaceNode::ToJson() const {
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700164 std::vector<llvm::json::Value> json_children;
165 json_children.reserve(children.size());
166 for (const auto& child : children) {
167 json_children.push_back(child.ToJson());
168 }
169
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700170 return llvm::json::Object{
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700171 {"name", name},
172 {"children", std::move(json_children)},
173 };
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700174}
175
Rosica Dejanovskaabe406f2022-09-02 07:06:50 -0700176llvm::json::Value NamespacesHierarchy::ToJson() const {
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700177 std::vector<llvm::json::Value> json_namespaces;
178 json_namespaces.reserve(namespaces.size());
179 for (const auto& ns : namespaces) {
180 json_namespaces.push_back(ns.ToJson());
181 }
182
183 return llvm::json::Object{
Rosica Dejanovska5b2e8132022-09-20 02:11:51 -0700184 {"label", label.value()},
Rosica Dejanovska8575a842022-09-01 02:05:30 -0700185 {"namespaces", std::move(json_namespaces)},
186 };
187}
188
189} // namespace crubit