1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_TORQUE_UTILS_H_
6 #define V8_TORQUE_UTILS_H_
7
8 #include <algorithm>
9 #include <ostream>
10 #include <queue>
11 #include <streambuf>
12 #include <string>
13 #include <unordered_set>
14
15 #include "src/base/functional.h"
16 #include "src/base/optional.h"
17 #include "src/torque/contextual.h"
18 #include "src/torque/source-positions.h"
19
20 namespace v8 {
21 namespace internal {
22 namespace torque {
23
24 std::string StringLiteralUnquote(const std::string& s);
25 std::string StringLiteralQuote(const std::string& s);
26
27 // Decodes "file://" URIs into file paths which can then be used
28 // with the standard stream API.
29 V8_EXPORT_PRIVATE base::Optional<std::string> FileUriDecode(
30 const std::string& s);
31
32 struct TorqueMessage {
33 enum class Kind { kError, kLint };
34
35 std::string message;
36 base::Optional<SourcePosition> position;
37 Kind kind;
38 };
39
40 DECLARE_CONTEXTUAL_VARIABLE(TorqueMessages, std::vector<TorqueMessage>);
41
42 template <class... Args>
ToString(Args &&...args)43 std::string ToString(Args&&... args) {
44 std::stringstream stream;
45 USE((stream << std::forward<Args>(args))...);
46 return stream.str();
47 }
48
49 class V8_EXPORT_PRIVATE MessageBuilder {
50 public:
51 MessageBuilder() = delete;
52 MessageBuilder(const std::string& message, TorqueMessage::Kind kind);
53
Position(SourcePosition position)54 MessageBuilder& Position(SourcePosition position) {
55 message_.position = position;
56 return *this;
57 }
58
59 [[noreturn]] void Throw() const;
60
~MessageBuilder()61 ~MessageBuilder() {
62 // This will also get called in case the error is thrown.
63 Report();
64 }
65
66 private:
67 void Report() const;
68
69 TorqueMessage message_;
70 std::vector<TorqueMessage> extra_messages_;
71 };
72
73 // Used for throwing exceptions. Retrieve TorqueMessage from the contextual
74 // for specific error information.
75 struct TorqueAbortCompilation {};
76
77 template <class... Args>
Message(TorqueMessage::Kind kind,Args &&...args)78 static MessageBuilder Message(TorqueMessage::Kind kind, Args&&... args) {
79 return MessageBuilder(ToString(std::forward<Args>(args)...), kind);
80 }
81
82 template <class... Args>
Error(Args &&...args)83 MessageBuilder Error(Args&&... args) {
84 return Message(TorqueMessage::Kind::kError, std::forward<Args>(args)...);
85 }
86 template <class... Args>
Lint(Args &&...args)87 MessageBuilder Lint(Args&&... args) {
88 return Message(TorqueMessage::Kind::kLint, std::forward<Args>(args)...);
89 }
90
91 bool IsLowerCamelCase(const std::string& s);
92 bool IsUpperCamelCase(const std::string& s);
93 bool IsSnakeCase(const std::string& s);
94 bool IsValidNamespaceConstName(const std::string& s);
95 bool IsValidTypeName(const std::string& s);
96
97 template <class... Args>
ReportError(Args &&...args)98 [[noreturn]] void ReportError(Args&&... args) {
99 Error(std::forward<Args>(args)...).Throw();
100 }
101
102 std::string CapifyStringWithUnderscores(const std::string& camellified_string);
103 std::string CamelifyString(const std::string& underscore_string);
104 std::string SnakeifyString(const std::string& camel_string);
105 std::string DashifyString(const std::string& underscore_string);
106 std::string UnderlinifyPath(std::string path);
107
108 bool StartsWithSingleUnderscore(const std::string& str);
109
110 void ReplaceFileContentsIfDifferent(const std::string& file_path,
111 const std::string& contents);
112
113 template <class T>
114 class Deduplicator {
115 public:
Add(T x)116 const T* Add(T x) { return &*(storage_.insert(std::move(x)).first); }
117
118 private:
119 std::unordered_set<T, base::hash<T>> storage_;
120 };
121
122 template <class T>
DereferenceIfPointer(T * x)123 T& DereferenceIfPointer(T* x) {
124 return *x;
125 }
126 template <class T>
DereferenceIfPointer(T && x)127 T&& DereferenceIfPointer(T&& x) {
128 return std::forward<T>(x);
129 }
130
131 template <class T, class L>
132 struct ListPrintAdaptor {
133 const T& list;
134 const std::string& separator;
135 L transformer;
136
137 friend std::ostream& operator<<(std::ostream& os, const ListPrintAdaptor& l) {
138 bool first = true;
139 for (auto& e : l.list) {
140 if (first) {
141 first = false;
142 } else {
143 os << l.separator;
144 }
145 os << DereferenceIfPointer(l.transformer(e));
146 }
147 return os;
148 }
149 };
150
151 template <class T>
152 auto PrintList(const T& list, const std::string& separator = ", ") {
153 using ElementType = decltype(*list.begin());
154 auto id = [](ElementType el) { return el; };
155 return ListPrintAdaptor<T, decltype(id)>{list, separator, id};
156 }
157
158 template <class T, class L>
PrintList(const T & list,const std::string & separator,L && transformer)159 auto PrintList(const T& list, const std::string& separator, L&& transformer) {
160 return ListPrintAdaptor<T, L&&>{list, separator,
161 std::forward<L>(transformer)};
162 }
163
164 template <class C, class T>
PrintCommaSeparatedList(std::ostream & os,const T & list,C && transform)165 void PrintCommaSeparatedList(std::ostream& os, const T& list, C&& transform) {
166 os << PrintList(list, ", ", std::forward<C>(transform));
167 }
168
169 template <class T>
PrintCommaSeparatedList(std::ostream & os,const T & list)170 void PrintCommaSeparatedList(std::ostream& os, const T& list) {
171 os << PrintList(list, ", ");
172 }
173
174 struct BottomOffset {
175 size_t offset;
176
177 BottomOffset& operator=(std::size_t other_offset) {
178 this->offset = other_offset;
179 return *this;
180 }
181 BottomOffset& operator++() {
182 ++offset;
183 return *this;
184 }
185 BottomOffset operator+(size_t x) const { return BottomOffset{offset + x}; }
186 BottomOffset operator-(size_t x) const {
187 DCHECK_LE(x, offset);
188 return BottomOffset{offset - x};
189 }
190 bool operator<(const BottomOffset& other) const {
191 return offset < other.offset;
192 }
193 bool operator<=(const BottomOffset& other) const {
194 return offset <= other.offset;
195 }
196 bool operator==(const BottomOffset& other) const {
197 return offset == other.offset;
198 }
199 bool operator!=(const BottomOffset& other) const {
200 return offset != other.offset;
201 }
202 };
203
204 inline std::ostream& operator<<(std::ostream& out, BottomOffset from_bottom) {
205 return out << "BottomOffset{" << from_bottom.offset << "}";
206 }
207
208 // An iterator-style range of stack slots.
209 class StackRange {
210 public:
StackRange(BottomOffset begin,BottomOffset end)211 StackRange(BottomOffset begin, BottomOffset end) : begin_(begin), end_(end) {
212 DCHECK_LE(begin_, end_);
213 }
214
215 bool operator==(const StackRange& other) const {
216 return begin_ == other.begin_ && end_ == other.end_;
217 }
218
Extend(StackRange adjacent)219 void Extend(StackRange adjacent) {
220 DCHECK_EQ(end_, adjacent.begin_);
221 end_ = adjacent.end_;
222 }
223
Size()224 size_t Size() const { return end_.offset - begin_.offset; }
begin()225 BottomOffset begin() const { return begin_; }
end()226 BottomOffset end() const { return end_; }
227
228 private:
229 BottomOffset begin_;
230 BottomOffset end_;
231 };
232
233 inline std::ostream& operator<<(std::ostream& out, StackRange range) {
234 return out << "StackRange{" << range.begin() << ", " << range.end() << "}";
235 }
236
237 template <class T>
238 class Stack {
239 public:
240 using value_type = T;
241 Stack() = default;
Stack(std::initializer_list<T> initializer)242 Stack(std::initializer_list<T> initializer)
243 : Stack(std::vector<T>(initializer)) {}
Stack(std::vector<T> v)244 explicit Stack(std::vector<T> v) : elements_(std::move(v)) {}
Size()245 size_t Size() const { return elements_.size(); }
Peek(BottomOffset from_bottom)246 const T& Peek(BottomOffset from_bottom) const {
247 return elements_.at(from_bottom.offset);
248 }
Poke(BottomOffset from_bottom,T x)249 void Poke(BottomOffset from_bottom, T x) {
250 elements_.at(from_bottom.offset) = std::move(x);
251 }
Push(T x)252 void Push(T x) {
253 elements_.push_back(std::move(x));
254 }
TopRange(size_t slot_count)255 StackRange TopRange(size_t slot_count) const {
256 DCHECK_GE(Size(), slot_count);
257 return StackRange{AboveTop() - slot_count, AboveTop()};
258 }
PushMany(const std::vector<T> & v)259 StackRange PushMany(const std::vector<T>& v) {
260 for (const T& x : v) {
261 Push(x);
262 }
263 return TopRange(v.size());
264 }
Top()265 const T& Top() const { return Peek(AboveTop() - 1); }
Pop()266 T Pop() {
267 T result = std::move(elements_.back());
268 elements_.pop_back();
269 return result;
270 }
PopMany(size_t count)271 std::vector<T> PopMany(size_t count) {
272 DCHECK_GE(elements_.size(), count);
273 std::vector<T> result;
274 result.reserve(count);
275 for (auto it = elements_.end() - count; it != elements_.end(); ++it) {
276 result.push_back(std::move(*it));
277 }
278 elements_.resize(elements_.size() - count);
279 return result;
280 }
281 // The invalid offset above the top element. This is useful for StackRange.
AboveTop()282 BottomOffset AboveTop() const { return BottomOffset{Size()}; }
283 // Delete the slots in {range}, moving higher slots to fill the gap.
DeleteRange(StackRange range)284 void DeleteRange(StackRange range) {
285 DCHECK_LE(range.end(), AboveTop());
286 if (range.Size() == 0) return;
287 for (BottomOffset i = range.end(); i < AboveTop(); ++i) {
288 elements_[i.offset - range.Size()] = std::move(elements_[i.offset]);
289 }
290 elements_.resize(elements_.size() - range.Size());
291 }
292
293 bool operator==(const Stack& other) const {
294 return elements_ == other.elements_;
295 }
296 bool operator!=(const Stack& other) const {
297 return elements_ != other.elements_;
298 }
299
begin()300 T* begin() { return elements_.data(); }
end()301 T* end() { return begin() + elements_.size(); }
begin()302 const T* begin() const { return elements_.data(); }
end()303 const T* end() const { return begin() + elements_.size(); }
304
305 private:
306 std::vector<T> elements_;
307 };
308
309 template <class T>
CheckNotNull(T * x)310 T* CheckNotNull(T* x) {
311 CHECK_NOT_NULL(x);
312 return x;
313 }
314
315 template <class T>
316 inline std::ostream& operator<<(std::ostream& os, const Stack<T>& t) {
317 os << "Stack{";
318 PrintCommaSeparatedList(os, t);
319 os << "}";
320 return os;
321 }
322
323 static const char* const kBaseNamespaceName = "base";
324 static const char* const kTestNamespaceName = "test";
325
326 // Erase elements of a container that has a constant-time erase function, like
327 // std::set or std::list. Calling this on std::vector would have quadratic
328 // complexity.
329 template <class Container, class F>
EraseIf(Container * container,F f)330 void EraseIf(Container* container, F f) {
331 for (auto it = container->begin(); it != container->end();) {
332 if (f(*it)) {
333 it = container->erase(it);
334 } else {
335 ++it;
336 }
337 }
338 }
339
340 class NullStreambuf : public std::streambuf {
341 public:
overflow(int c)342 int overflow(int c) override {
343 setp(buffer_, buffer_ + sizeof(buffer_));
344 return (c == traits_type::eof()) ? '\0' : c;
345 }
346
347 private:
348 char buffer_[64];
349 };
350
351 class NullOStream : public std::ostream {
352 public:
NullOStream()353 NullOStream() : std::ostream(&buffer_) {}
354
355 private:
356 NullStreambuf buffer_;
357 };
358
StringStartsWith(const std::string & s,const std::string & prefix)359 inline bool StringStartsWith(const std::string& s, const std::string& prefix) {
360 if (s.size() < prefix.size()) return false;
361 return s.substr(0, prefix.size()) == prefix;
362 }
StringEndsWith(const std::string & s,const std::string & suffix)363 inline bool StringEndsWith(const std::string& s, const std::string& suffix) {
364 if (s.size() < suffix.size()) return false;
365 return s.substr(s.size() - suffix.size()) == suffix;
366 }
367
368 class V8_NODISCARD IfDefScope {
369 public:
370 IfDefScope(std::ostream& os, std::string d);
371 ~IfDefScope();
372 IfDefScope(const IfDefScope&) = delete;
373 IfDefScope& operator=(const IfDefScope&) = delete;
374
375 private:
376 std::ostream& os_;
377 std::string d_;
378 };
379
380 class V8_NODISCARD NamespaceScope {
381 public:
382 NamespaceScope(std::ostream& os,
383 std::initializer_list<std::string> namespaces);
384 ~NamespaceScope();
385 NamespaceScope(const NamespaceScope&) = delete;
386 NamespaceScope& operator=(const NamespaceScope&) = delete;
387
388 private:
389 std::ostream& os_;
390 std::vector<std::string> d_;
391 };
392
393 class V8_NODISCARD IncludeGuardScope {
394 public:
395 IncludeGuardScope(std::ostream& os, std::string file_name);
396 ~IncludeGuardScope();
397 IncludeGuardScope(const IncludeGuardScope&) = delete;
398 IncludeGuardScope& operator=(const IncludeGuardScope&) = delete;
399
400 private:
401 std::ostream& os_;
402 std::string d_;
403 };
404
405 class V8_NODISCARD IncludeObjectMacrosScope {
406 public:
407 explicit IncludeObjectMacrosScope(std::ostream& os);
408 ~IncludeObjectMacrosScope();
409 IncludeObjectMacrosScope(const IncludeObjectMacrosScope&) = delete;
410 IncludeObjectMacrosScope& operator=(const IncludeObjectMacrosScope&) = delete;
411
412 private:
413 std::ostream& os_;
414 };
415
416 // A value of ResidueClass is a congruence class of integers modulo a power
417 // of 2.
418 // In contrast to common modulo arithmetic, we also allow addition and
419 // multiplication of congruence classes with different modulus. In this case, we
420 // do an abstract-interpretation style approximation to produce an as small as
421 // possible congruence class. ResidueClass is used to represent partial
422 // knowledge about offsets and sizes to validate alignment constraints.
423 // ResidueClass(x,m) = {y \in Z | x == y mod 2^m} = {x+k2^m | k \in Z} where Z
424 // is the set of all integers.
425 // Notation: 2^x is 2 to the power of x.
426 class ResidueClass {
427 public:
428 ResidueClass(size_t value, size_t modulus_log_2 =
429 kMaxModulusLog2) // NOLINT(runtime/explicit)
value_(value)430 : value_(value),
431 modulus_log_2_(std::min(modulus_log_2, kMaxModulusLog2)) {
432 if (modulus_log_2_ < kMaxModulusLog2) {
433 value_ %= size_t{1} << modulus_log_2_;
434 }
435 }
436
437 // 0 modulo 1, in other words, the class of all integers.
Unknown()438 static ResidueClass Unknown() { return ResidueClass{0, 0}; }
439
440 // If the modulus corresponds to the size of size_t, it represents a concrete
441 // value.
SingleValue()442 base::Optional<size_t> SingleValue() const {
443 if (modulus_log_2_ == kMaxModulusLog2) return value_;
444 return base::nullopt;
445 }
446
447 friend ResidueClass operator+(const ResidueClass& a, const ResidueClass& b) {
448 return ResidueClass{a.value_ + b.value_,
449 std::min(a.modulus_log_2_, b.modulus_log_2_)};
450 }
451
452 // Reasoning for the choice of the new modulus:
453 // {x+k2^a | k \in Z} * {y+l2^b | l \in Z}
454 // = {xy + xl2^b + yk2^a + kl2^(a+b)| k,l \in Z},
455 // which is a subset of {xy + k2^c | k \in Z}
456 // if 2^c is a common divisor of x2^b, y2^a and hence also of 2^(a+b) since
457 // x<2^a and y<2^b.
458 // So we use the gcd of x2^b and y2^a as the new modulus.
459 friend ResidueClass operator*(const ResidueClass& a, const ResidueClass& b) {
460 return ResidueClass{a.value_ * b.value_,
461 std::min(a.modulus_log_2_ + b.AlignmentLog2(),
462 b.modulus_log_2_ + a.AlignmentLog2())};
463 }
464
465 friend std::ostream& operator<<(std::ostream& os, const ResidueClass& a);
466
467 ResidueClass& operator+=(const ResidueClass& other) {
468 *this = *this + other;
469 return *this;
470 }
471
472 ResidueClass& operator*=(const ResidueClass& other) {
473 *this = *this * other;
474 return *this;
475 }
476
477 // 2^AlignmentLog2() is the larget power of 2 that divides all elements of the
478 // congruence class.
479 size_t AlignmentLog2() const;
Alignment()480 size_t Alignment() const {
481 DCHECK_LT(AlignmentLog2(), kMaxModulusLog2);
482 return size_t{1} << AlignmentLog2();
483 }
484
485 private:
486 // The value is the representative of the congruence class. It's always
487 // smaller than 2^modulus_log_2_.
488 size_t value_;
489 // Base 2 logarithm of the modulus.
490 size_t modulus_log_2_;
491
492 // size_t values are modulo 2^kMaxModulusLog2, so we don't consider larger
493 // modulus.
494 static const size_t kMaxModulusLog2 = 8 * sizeof(size_t);
495 };
496
497 template <typename T>
498 class Worklist {
499 public:
IsEmpty()500 bool IsEmpty() const {
501 DCHECK_EQ(queue_.size(), contained_.size());
502 return queue_.empty();
503 }
504
Enqueue(T value)505 bool Enqueue(T value) {
506 if (contained_.find(value) != contained_.end()) return false;
507 queue_.push(value);
508 contained_.insert(value);
509 DCHECK_EQ(queue_.size(), contained_.size());
510 return true;
511 }
512
Dequeue()513 T Dequeue() {
514 DCHECK(!IsEmpty());
515 T value = queue_.front();
516 queue_.pop();
517 contained_.erase(value);
518 DCHECK_EQ(queue_.size(), contained_.size());
519 return value;
520 }
521
522 private:
523 std::queue<T> queue_;
524 std::unordered_set<T> contained_;
525 };
526
527 template <class T, class U, class F>
TransformVector(const std::vector<U> & v,F f)528 std::vector<T> TransformVector(const std::vector<U>& v, F f) {
529 std::vector<T> result;
530 std::transform(v.begin(), v.end(), std::back_inserter(result), f);
531 return result;
532 }
533 template <class T, class U>
TransformVector(const std::vector<U> & v)534 std::vector<T> TransformVector(const std::vector<U>& v) {
535 return TransformVector<T>(v, [](const U& x) -> T { return x; });
536 }
537
538 } // namespace torque
539 } // namespace internal
540 } // namespace v8
541
542 #endif // V8_TORQUE_UTILS_H_
543