Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 1 | // 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 Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 13 | #include "rs_bindings_from_cc/bazel_types.h" |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 14 | #include "rs_bindings_from_cc/ir.h" |
Rosica Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 15 | #include "llvm/Support/JSON.h" |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 16 | |
| 17 | namespace crubit { |
| 18 | namespace { |
| 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. |
| 41 | class 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 Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 56 | // The current target's label. |
| 57 | BazelLabel label_; |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 58 | // 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 Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 86 | // Converts a trie node into the JSON serializable NamespaceNode. |
| 87 | NamespaceNode NodeToNamespaceNode(const Node* node) const { |
| 88 | std::vector<NamespaceNode> namespaces; |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 89 | namespaces.reserve(node->child_name_to_idx.size()); |
| 90 | for (const auto& [_, idx] : node->child_name_to_idx) { |
Rosica Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 91 | namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx])); |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 92 | } |
Rosica Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 93 | return NamespaceNode{std::string(node->name), std::move(namespaces)}; |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | public: |
Rosica Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 97 | NamespaceTrie(BazelLabel label, |
| 98 | absl::flat_hash_map<ItemId, const Namespace*>& id_to_namespace) |
| 99 | : label_(label), id_to_namespace_(id_to_namespace) {} |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 100 | |
| 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 Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 126 | // Converts the trie into the JSON serializable NamespacesHierarchy. |
| 127 | NamespacesHierarchy ToNamespacesHierarchy() { |
| 128 | std::vector<NamespaceNode> namespaces; |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 129 | namespaces.reserve(this->top_level_name_to_idx_.size()); |
| 130 | for (auto& [_, idx] : this->top_level_name_to_idx_) { |
Rosica Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 131 | namespaces.push_back(NodeToNamespaceNode(&trie_nodes_[idx])); |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 132 | } |
Rosica Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 133 | return NamespacesHierarchy{label_, std::move(namespaces)}; |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 134 | } |
| 135 | }; |
| 136 | |
| 137 | } // namespace |
| 138 | |
| 139 | // Returns the current target's namespace hierarchy in JSON serializable format. |
Rosica Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 140 | NamespacesHierarchy CollectNamespaces(const IR& ir) { |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 141 | 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 Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 151 | NamespaceTrie trie(ir.current_target, id_to_namespace); |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 152 | 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 Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 160 | return trie.ToNamespacesHierarchy(); |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 161 | } |
| 162 | |
Rosica Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 163 | llvm::json::Value NamespaceNode::ToJson() const { |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 164 | 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 Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 170 | return llvm::json::Object{ |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 171 | {"name", name}, |
| 172 | {"children", std::move(json_children)}, |
| 173 | }; |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 174 | } |
| 175 | |
Rosica Dejanovska | abe406f | 2022-09-02 07:06:50 -0700 | [diff] [blame] | 176 | llvm::json::Value NamespacesHierarchy::ToJson() const { |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 177 | 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 Dejanovska | 5b2e813 | 2022-09-20 02:11:51 -0700 | [diff] [blame] | 184 | {"label", label.value()}, |
Rosica Dejanovska | 8575a84 | 2022-09-01 02:05:30 -0700 | [diff] [blame] | 185 | {"namespaces", std::move(json_namespaces)}, |
| 186 | }; |
| 187 | } |
| 188 | |
| 189 | } // namespace crubit |