Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/include/hermes/ADT/ScopedHashTable.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_ADT_SCOPEDHASHTABLE_H
9
#define HERMES_ADT_SCOPEDHASHTABLE_H
10
11
#include "llvh/ADT/DenseMap.h"
12
#include "llvh/Support/Allocator.h"
13
14
// hermes::ScopedHashTable is a drop-in replacement for llvh::ScopedHashTable,
15
// but will allow us to export the current values in scope.
16
17
namespace hermes {
18
template <typename K, typename V>
19
class ScopedHashTableNode {
20
 public:
21
  K key_;
22
  V value_;
23
24
  ScopedHashTableNode<K, V> *nextShadowed_{nullptr};
25
  ScopedHashTableNode<K, V> *nextInScope_{nullptr};
26
  uint32_t depth_;
27
  ScopedHashTableNode(uint32_t depth, const K &key, const V &value)
28
32.9k
      : key_(key), value_(value), depth_(depth) {}
29
};
30
31
template <typename K, typename V>
32
class ScopedHashTable;
33
34
template <typename K, typename V>
35
class ScopedHashTableScope {
36
 private:
37
  uint32_t depth_;
38
  /// Start of the linked list of nodes in this scope. Owned by ScopedHashTable.
39
  ScopedHashTableNode<K, V> *head_{nullptr};
40
  /// The scope we're shadowing. Owned by the user.
41
  ScopedHashTableScope<K, V> *previous_;
42
  /// The table we're creating a scope for.
43
  ScopedHashTable<K, V> &base_;
44
45
 public:
46
  ScopedHashTableScope(ScopedHashTable<K, V> &base);
47
  ~ScopedHashTableScope();
48
49
  ScopedHashTableScope(const ScopedHashTableScope &) = delete;
50
  ScopedHashTableScope(ScopedHashTableScope &&) = delete;
51
  ScopedHashTableScope &operator=(const ScopedHashTableScope &) = delete;
52
  ScopedHashTableScope &operator=(ScopedHashTableScope &&) = delete;
53
54
  friend class ScopedHashTable<K, V>;
55
};
56
57
template <typename K, typename V>
58
class ScopedHashTable {
59
 private:
60
  /// Maps from keys to most current definition. All Nodes are owned by this.
61
  /// They are allocated by insertIntoScope and deleted by clearCurrentScope
62
  llvh::DenseMap<K, ScopedHashTableNode<K, V> *> map_;
63
  // The current scope. Owned by the user.
64
  ScopedHashTableScope<K, V> *scope_{nullptr};
65
66
  /// Unlinks the innermost Node for a key and returns it.
67
32.9k
  ScopedHashTableNode<K, V> *pop(const K &key) {
68
32.9k
    ScopedHashTableNode<K, V> *&entry = map_[key];
69
32.9k
    assert(entry && "Asked to pop an empty scope value");
70
32.9k
    assert(
71
32.9k
        entry->depth_ == scope_->depth_ &&
72
32.9k
        "Asked to pop value not from innermost scope");
73
32.9k
    auto *ret = entry;
74
32.9k
    if (entry->nextShadowed_) {
75
21.3k
      entry = entry->nextShadowed_;
76
21.3k
    } else {
77
11.5k
      map_.erase(key);
78
11.5k
    }
79
32.9k
    return ret;
80
32.9k
  }
81
82
  /// Unlinks and deallocates all keys in the current scope.
83
23.2k
  void clearCurrentScope() {
84
23.2k
    assert(scope_ && "No current scope to clear");
85
86
23.2k
    auto *current = scope_->head_;
87
56.1k
    while (current) {
88
32.9k
      assert(current->depth_ == scope_->depth_ && "Bad scope link");
89
32.9k
      auto *popped = pop(current->key_);
90
32.9k
      assert(current == popped && "Unexpected innermost value for key");
91
32.9k
      current = current->nextInScope_;
92
      // All nodes deallocated here.
93
32.9k
      delete popped;
94
32.9k
    }
95
23.2k
    scope_->head_ = nullptr;
96
23.2k
  }
97
98
  /// Create a new node and insert it into the current scope.
99
  void insertNewNode(
100
      ScopedHashTableScope<K, V> *const scope,
101
      const K &key,
102
      const V &value,
103
32.9k
      ScopedHashTableNode<K, V> *&entry) {
104
    // All Nodes allocated here.
105
32.9k
    auto *update = new ScopedHashTableNode<K, V>(scope->depth_, key, value);
106
32.9k
    assert(
107
32.9k
        (!entry || entry->depth_ <= scope->depth_) &&
108
32.9k
        "Can't insert values under existing names");
109
32.9k
    update->nextShadowed_ = entry;
110
32.9k
    update->nextInScope_ = scope->head_;
111
32.9k
    scope->head_ = update;
112
32.9k
    entry = update;
113
32.9k
  }
114
115
 public:
116
147
  ScopedHashTable() : map_() {}
117
147
  ~ScopedHashTable() {
118
147
    assert(!scope_ && "Scopes remain when destructing ScopedHashTable");
119
147
    assert(!map_.size() && "Elements remaining in map without scope!");
120
147
  }
121
122
  /// Inserts a new key into the given scope. A key may not be inserted
123
  /// such that it would be shadowed by another scope currently in effect.
124
  void insertIntoScope(
125
      ScopedHashTableScope<K, V> *const scope,
126
      const K &key,
127
32.9k
      const V &value) {
128
32.9k
    assert(scope && "No currently defined scope");
129
32.9k
    insertNewNode(scope, key, value, map_[key]);
130
32.9k
  }
131
132
  /// Inserts a value into the current scope.
133
23.0k
  void insert(const K &key, const V &value) {
134
23.0k
    insertIntoScope(scope_, key, value);
135
23.0k
  }
136
137
  /// If the key exists in the current scope, update its value to the specified
138
  /// one. Otherwise, create a new entry in current scope.
139
0
  void setInCurrentScope(const K &key, const V &value) {
140
0
    assert(scope_ && "No currently defined scope");
141
0
    ScopedHashTableNode<K, V> *&entry = map_[key];
142
0
    if (entry && entry->depth_ == scope_->depth_) {
143
      // If the key exists in the current scope, update the value.
144
0
      entry->value_ = value;
145
0
    } else {
146
      // Otherwise, create a new node in the current scope.
147
0
      insertNewNode(scope_, key, value, entry);
148
0
    }
149
0
  }
150
151
  /// Returns 1 if the value is defined, 0 if it's not.
152
15
  uint32_t count(const K &key) const {
153
15
    return map_.count(key);
154
15
  }
155
156
  /// Gets the innermost value for a key, or default value if none.
157
431k
  V lookup(const K &key) const {
158
431k
    auto result = map_.find(key);
159
431k
    if (result == map_.end())
160
17.3k
      return V();
161
162
414k
    return result->second->value_;
163
431k
  }
164
165
  // Gets all values currently in scope.
166
  std::unique_ptr<llvh::DenseMap<K, V>> flatten() const {
167
    std::unique_ptr<llvh::DenseMap<K, V>> result{
168
        new llvh::DenseMap<K, V>(map_.size())};
169
    for (auto &pair : map_) {
170
      assert(pair.second && "Node is null");
171
      (*result)[pair.first] = pair.second->value_;
172
    }
173
    return result;
174
  }
175
176
  // Gets keys in each scope. This may correspond to a \p ScopeChain.
177
  // Shadowed keys are ignored. 0 is innermost.
178
  std::unique_ptr<std::vector<std::vector<K>>> getKeysByScope() const {
179
    assert(scope_ && "Missing scope");
180
181
    int size = scope_->depth_ + 1;
182
    std::unique_ptr<std::vector<std::vector<K>>> result;
183
    result.reset(new std::vector<std::vector<K>>());
184
    result->resize(size);
185
186
    for (auto &pair : map_) {
187
      auto *node = pair.second;
188
      assert(node && "Node is null");
189
      assert(node->depth_ <= scope_->depth_ && "Node at bad depth");
190
      result->at(size - node->depth_ - 1).push_back(pair.first);
191
    }
192
    return result;
193
  }
194
195
  friend class ScopedHashTableScope<K, V>;
196
};
197
198
template <typename K, typename V>
199
ScopedHashTableScope<K, V>::ScopedHashTableScope(ScopedHashTable<K, V> &base)
200
23.2k
    : base_(base) {
201
23.2k
  previous_ = base.scope_;
202
23.2k
  depth_ = !previous_ ? 0 : previous_->depth_ + 1;
203
23.2k
  base.scope_ = this;
204
23.2k
}
205
206
template <typename K, typename V>
207
23.2k
ScopedHashTableScope<K, V>::~ScopedHashTableScope() {
208
23.2k
  assert(this == base_.scope_ && "Deallocating scope that's not current!");
209
23.2k
  base_.clearCurrentScope();
210
23.2k
  base_.scope_ = previous_;
211
23.2k
}
212
213
} // namespace hermes
214
215
#endif // HERMES_ADT_SCOPEDHASHTABLE_H