• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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