• 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 #include <algorithm>
6 #include <fstream>
7 #include <iostream>
8 #include <string>
9 
10 #include "src/base/bits.h"
11 #include "src/base/logging.h"
12 #include "src/torque/ast.h"
13 #include "src/torque/constants.h"
14 #include "src/torque/declarable.h"
15 #include "src/torque/utils.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace torque {
20 
DEFINE_CONTEXTUAL_VARIABLE(TorqueMessages)21 DEFINE_CONTEXTUAL_VARIABLE(TorqueMessages)
22 
23 std::string StringLiteralUnquote(const std::string& s) {
24   DCHECK(('"' == s.front() && '"' == s.back()) ||
25          ('\'' == s.front() && '\'' == s.back()));
26   std::stringstream result;
27   for (size_t i = 1; i < s.length() - 1; ++i) {
28     if (s[i] == '\\') {
29       switch (s[++i]) {
30         case 'n':
31           result << '\n';
32           break;
33         case 'r':
34           result << '\r';
35           break;
36         case 't':
37           result << '\t';
38           break;
39         case '\'':
40         case '"':
41         case '\\':
42           result << s[i];
43           break;
44         default:
45           UNREACHABLE();
46       }
47     } else {
48       result << s[i];
49     }
50   }
51   return result.str();
52 }
53 
StringLiteralQuote(const std::string & s)54 std::string StringLiteralQuote(const std::string& s) {
55   std::stringstream result;
56   result << '"';
57   for (size_t i = 0; i < s.length(); ++i) {
58     switch (s[i]) {
59       case '\n':
60         result << "\\n";
61         break;
62       case '\r':
63         result << "\\r";
64         break;
65       case '\t':
66         result << "\\t";
67         break;
68       case '"':
69       case '\\':
70         result << "\\" << s[i];
71         break;
72       default:
73         result << s[i];
74     }
75   }
76   result << '"';
77   return result.str();
78 }
79 
80 #ifdef V8_OS_WIN
81 static const char kFileUriPrefix[] = "file:///";
82 #else
83 static const char kFileUriPrefix[] = "file://";
84 #endif
85 static const int kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1;
86 
HexCharToInt(unsigned char c)87 static int HexCharToInt(unsigned char c) {
88   if (isdigit(c)) return c - '0';
89   if (isupper(c)) return c - 'A' + 10;
90   DCHECK(islower(c));
91   return c - 'a' + 10;
92 }
93 
FileUriDecode(const std::string & uri)94 base::Optional<std::string> FileUriDecode(const std::string& uri) {
95   // Abort decoding of URIs that don't start with "file://".
96   if (uri.rfind(kFileUriPrefix) != 0) return base::nullopt;
97 
98   const std::string path = uri.substr(kFileUriPrefixLength);
99   std::ostringstream decoded;
100 
101   for (auto iter = path.begin(), end = path.end(); iter != end; ++iter) {
102     std::string::value_type c = (*iter);
103 
104     // Normal characters are appended.
105     if (c != '%') {
106       decoded << c;
107       continue;
108     }
109 
110     // If '%' is not followed by at least two hex digits, we abort.
111     if (std::distance(iter, end) <= 2) return base::nullopt;
112 
113     unsigned char first = (*++iter);
114     unsigned char second = (*++iter);
115     if (!isxdigit(first) || !isxdigit(second)) return base::nullopt;
116 
117     // An escaped hex value needs converting.
118     unsigned char value = HexCharToInt(first) * 16 + HexCharToInt(second);
119     decoded << value;
120   }
121 
122   return decoded.str();
123 }
124 
MessageBuilder(const std::string & message,TorqueMessage::Kind kind)125 MessageBuilder::MessageBuilder(const std::string& message,
126                                TorqueMessage::Kind kind) {
127   base::Optional<SourcePosition> position;
128   if (CurrentSourcePosition::HasScope()) {
129     position = CurrentSourcePosition::Get();
130   }
131   message_ = TorqueMessage{message, position, kind};
132   if (CurrentScope::HasScope()) {
133     // Traverse the parent scopes to find one that was created to represent a
134     // specialization of something generic. If we find one, then log it and
135     // continue walking the scope tree of the code that requested that
136     // specialization. This allows us to collect the stack of locations that
137     // caused a specialization.
138     Scope* scope = CurrentScope::Get();
139     while (scope) {
140       SpecializationRequester requester = scope->GetSpecializationRequester();
141       if (!requester.IsNone()) {
142         extra_messages_.push_back(
143             {"Note: in specialization " + requester.name + " requested here",
144              requester.position, kind});
145         scope = requester.scope;
146       } else {
147         scope = scope->ParentScope();
148       }
149     }
150   }
151 }
152 
Report() const153 void MessageBuilder::Report() const {
154   TorqueMessages::Get().push_back(message_);
155   for (const auto& message : extra_messages_) {
156     TorqueMessages::Get().push_back(message);
157   }
158 }
159 
Throw() const160 [[noreturn]] void MessageBuilder::Throw() const {
161   throw TorqueAbortCompilation{};
162 }
163 
164 namespace {
165 
ContainsUnderscore(const std::string & s)166 bool ContainsUnderscore(const std::string& s) {
167   if (s.empty()) return false;
168   return s.find("_") != std::string::npos;
169 }
170 
ContainsUpperCase(const std::string & s)171 bool ContainsUpperCase(const std::string& s) {
172   if (s.empty()) return false;
173   return std::any_of(s.begin(), s.end(), [](char c) { return isupper(c); });
174 }
175 
176 // Torque has some namespace constants that are used like language level
177 // keywords, e.g.: 'True', 'Undefined', etc.
178 // These do not need to follow the default naming convention for constants.
IsKeywordLikeName(const std::string & s)179 bool IsKeywordLikeName(const std::string& s) {
180   static const char* const keyword_like_constants[]{"True", "False", "TheHole",
181                                                     "Null", "Undefined"};
182 
183   return std::find(std::begin(keyword_like_constants),
184                    std::end(keyword_like_constants),
185                    s) != std::end(keyword_like_constants);
186 }
187 
188 // Untagged/MachineTypes like 'int32', 'intptr' etc. follow a 'all-lowercase'
189 // naming convention and are those exempt from the normal type convention.
IsMachineType(const std::string & s)190 bool IsMachineType(const std::string& s) {
191   static const char* const machine_types[]{VOID_TYPE_STRING,
192                                            NEVER_TYPE_STRING,
193                                            INT8_TYPE_STRING,
194                                            UINT8_TYPE_STRING,
195                                            INT16_TYPE_STRING,
196                                            UINT16_TYPE_STRING,
197                                            INT31_TYPE_STRING,
198                                            UINT31_TYPE_STRING,
199                                            INT32_TYPE_STRING,
200                                            UINT32_TYPE_STRING,
201                                            INT64_TYPE_STRING,
202                                            UINT64_TYPE_STRING,
203                                            INTPTR_TYPE_STRING,
204                                            UINTPTR_TYPE_STRING,
205                                            FLOAT32_TYPE_STRING,
206                                            FLOAT64_TYPE_STRING,
207                                            FLOAT64_OR_HOLE_TYPE_STRING,
208                                            BOOL_TYPE_STRING,
209                                            "string",
210                                            BINT_TYPE_STRING,
211                                            CHAR8_TYPE_STRING,
212                                            CHAR16_TYPE_STRING};
213   return std::find(std::begin(machine_types), std::end(machine_types), s) !=
214          std::end(machine_types);
215 }
216 
217 }  // namespace
218 
IsLowerCamelCase(const std::string & s)219 bool IsLowerCamelCase(const std::string& s) {
220   if (s.empty()) return false;
221   size_t start = 0;
222   if (s[0] == '_') start = 1;
223   return islower(s[start]) && !ContainsUnderscore(s.substr(start));
224 }
225 
IsUpperCamelCase(const std::string & s)226 bool IsUpperCamelCase(const std::string& s) {
227   if (s.empty()) return false;
228   size_t start = 0;
229   if (s[0] == '_') start = 1;
230   return isupper(s[start]);
231 }
232 
IsSnakeCase(const std::string & s)233 bool IsSnakeCase(const std::string& s) {
234   if (s.empty()) return false;
235   return !ContainsUpperCase(s);
236 }
237 
IsValidNamespaceConstName(const std::string & s)238 bool IsValidNamespaceConstName(const std::string& s) {
239   if (s.empty()) return false;
240   if (IsKeywordLikeName(s)) return true;
241 
242   return s[0] == 'k' && IsUpperCamelCase(s.substr(1));
243 }
244 
IsValidTypeName(const std::string & s)245 bool IsValidTypeName(const std::string& s) {
246   if (s.empty()) return false;
247   if (IsMachineType(s)) return true;
248 
249   return IsUpperCamelCase(s);
250 }
251 
CapifyStringWithUnderscores(const std::string & camellified_string)252 std::string CapifyStringWithUnderscores(const std::string& camellified_string) {
253   // Special case: JSAbc yields JS_ABC, not JSABC, for any Abc.
254   size_t js_position = camellified_string.find("JS");
255 
256   std::string result;
257   bool previousWasLowerOrDigit = false;
258   for (size_t index = 0; index < camellified_string.size(); ++index) {
259     char current = camellified_string[index];
260     if ((previousWasLowerOrDigit && isupper(current)) ||
261         (js_position != std::string::npos &&
262          index == js_position + strlen("JS"))) {
263       result += "_";
264     }
265     if (current == '.' || current == '-') {
266       result += "_";
267       previousWasLowerOrDigit = false;
268       continue;
269     }
270     result += toupper(current);
271     previousWasLowerOrDigit = islower(current) || isdigit(current);
272   }
273   return result;
274 }
275 
CamelifyString(const std::string & underscore_string)276 std::string CamelifyString(const std::string& underscore_string) {
277   std::string result;
278   bool word_beginning = true;
279   for (auto current : underscore_string) {
280     if (current == '_' || current == '-') {
281       word_beginning = true;
282       continue;
283     }
284     if (word_beginning) {
285       current = toupper(current);
286     }
287     result += current;
288     word_beginning = false;
289   }
290   return result;
291 }
292 
SnakeifyString(const std::string & camel_string)293 std::string SnakeifyString(const std::string& camel_string) {
294   std::string result;
295   bool previousWasLower = false;
296   for (auto current : camel_string) {
297     if (previousWasLower && isupper(current)) {
298       result += "_";
299     }
300     result += tolower(current);
301     previousWasLower = (islower(current));
302   }
303   return result;
304 }
305 
DashifyString(const std::string & underscore_string)306 std::string DashifyString(const std::string& underscore_string) {
307   std::string result = underscore_string;
308   std::replace(result.begin(), result.end(), '_', '-');
309   return result;
310 }
311 
UnderlinifyPath(std::string path)312 std::string UnderlinifyPath(std::string path) {
313   std::replace(path.begin(), path.end(), '-', '_');
314   std::replace(path.begin(), path.end(), '/', '_');
315   std::replace(path.begin(), path.end(), '\\', '_');
316   std::replace(path.begin(), path.end(), '.', '_');
317   transform(path.begin(), path.end(), path.begin(), ::toupper);
318   return path;
319 }
320 
StartsWithSingleUnderscore(const std::string & str)321 bool StartsWithSingleUnderscore(const std::string& str) {
322   return str.length() >= 2 && str[0] == '_' && str[1] != '_';
323 }
324 
ReplaceFileContentsIfDifferent(const std::string & file_path,const std::string & contents)325 void ReplaceFileContentsIfDifferent(const std::string& file_path,
326                                     const std::string& contents) {
327   std::ifstream old_contents_stream(file_path.c_str());
328   std::string old_contents;
329   bool file_exists = false;
330   if (old_contents_stream.good()) {
331     file_exists = true;
332     std::istreambuf_iterator<char> eos;
333     old_contents =
334         std::string(std::istreambuf_iterator<char>(old_contents_stream), eos);
335     old_contents_stream.close();
336   }
337   if (!file_exists || old_contents != contents) {
338     std::ofstream new_contents_stream;
339     new_contents_stream.open(file_path.c_str());
340     new_contents_stream << contents;
341     new_contents_stream.close();
342   }
343 }
344 
IfDefScope(std::ostream & os,std::string d)345 IfDefScope::IfDefScope(std::ostream& os, std::string d)
346     : os_(os), d_(std::move(d)) {
347   os_ << "#ifdef " << d_ << "\n";
348 }
~IfDefScope()349 IfDefScope::~IfDefScope() { os_ << "#endif  // " << d_ << "\n"; }
350 
NamespaceScope(std::ostream & os,std::initializer_list<std::string> namespaces)351 NamespaceScope::NamespaceScope(std::ostream& os,
352                                std::initializer_list<std::string> namespaces)
353     : os_(os), d_(std::move(namespaces)) {
354   for (const std::string& s : d_) {
355     os_ << "namespace " << s << " {\n";
356   }
357 }
~NamespaceScope()358 NamespaceScope::~NamespaceScope() {
359   for (auto i = d_.rbegin(); i != d_.rend(); ++i) {
360     os_ << "}  // namespace " << *i << "\n";
361   }
362 }
363 
IncludeGuardScope(std::ostream & os,std::string file_name)364 IncludeGuardScope::IncludeGuardScope(std::ostream& os, std::string file_name)
365     : os_(os),
366       d_("V8_GEN_TORQUE_GENERATED_" + CapifyStringWithUnderscores(file_name) +
367          "_") {
368   os_ << "#ifndef " << d_ << "\n";
369   os_ << "#define " << d_ << "\n\n";
370 }
~IncludeGuardScope()371 IncludeGuardScope::~IncludeGuardScope() { os_ << "#endif  // " << d_ << "\n"; }
372 
IncludeObjectMacrosScope(std::ostream & os)373 IncludeObjectMacrosScope::IncludeObjectMacrosScope(std::ostream& os) : os_(os) {
374   os_ << "\n// Has to be the last include (doesn't have include guards):\n"
375          "#include \"src/objects/object-macros.h\"\n";
376 }
~IncludeObjectMacrosScope()377 IncludeObjectMacrosScope::~IncludeObjectMacrosScope() {
378   os_ << "\n#include \"src/objects/object-macros-undef.h\"\n";
379 }
380 
AlignmentLog2() const381 size_t ResidueClass::AlignmentLog2() const {
382   if (value_ == 0) return modulus_log_2_;
383   return base::bits::CountTrailingZeros(value_);
384 }
385 
386 const size_t ResidueClass::kMaxModulusLog2;
387 
operator <<(std::ostream & os,const ResidueClass & a)388 std::ostream& operator<<(std::ostream& os, const ResidueClass& a) {
389   if (a.SingleValue().has_value()) return os << *a.SingleValue();
390   return os << "[" << a.value_ << " mod 2^" << a.modulus_log_2_ << "]";
391 }
392 
393 }  // namespace torque
394 }  // namespace internal
395 }  // namespace v8
396