• 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 
CurrentPositionAsString()125 std::string CurrentPositionAsString() {
126   return PositionAsString(CurrentSourcePosition::Get());
127 }
128 
MessageBuilder(const std::string & message,TorqueMessage::Kind kind)129 MessageBuilder::MessageBuilder(const std::string& message,
130                                TorqueMessage::Kind kind) {
131   base::Optional<SourcePosition> position;
132   if (CurrentSourcePosition::HasScope()) {
133     position = CurrentSourcePosition::Get();
134   }
135   message_ = TorqueMessage{message, position, kind};
136   if (CurrentScope::HasScope()) {
137     // Traverse the parent scopes to find one that was created to represent a
138     // specialization of something generic. If we find one, then log it and
139     // continue walking the scope tree of the code that requested that
140     // specialization. This allows us to collect the stack of locations that
141     // caused a specialization.
142     Scope* scope = CurrentScope::Get();
143     while (scope) {
144       SpecializationRequester requester = scope->GetSpecializationRequester();
145       if (!requester.IsNone()) {
146         extra_messages_.push_back(
147             {"Note: in specialization " + requester.name + " requested here",
148              requester.position, kind});
149         scope = requester.scope;
150       } else {
151         scope = scope->ParentScope();
152       }
153     }
154   }
155 }
156 
Report() const157 void MessageBuilder::Report() const {
158   TorqueMessages::Get().push_back(message_);
159   for (const auto& message : extra_messages_) {
160     TorqueMessages::Get().push_back(message);
161   }
162 }
163 
Throw() const164 [[noreturn]] void MessageBuilder::Throw() const {
165   throw TorqueAbortCompilation{};
166 }
167 
168 namespace {
169 
ContainsUnderscore(const std::string & s)170 bool ContainsUnderscore(const std::string& s) {
171   if (s.empty()) return false;
172   return s.find("_") != std::string::npos;
173 }
174 
ContainsUpperCase(const std::string & s)175 bool ContainsUpperCase(const std::string& s) {
176   if (s.empty()) return false;
177   return std::any_of(s.begin(), s.end(), [](char c) { return isupper(c); });
178 }
179 
180 // Torque has some namespace constants that are used like language level
181 // keywords, e.g.: 'True', 'Undefined', etc.
182 // These do not need to follow the default naming convention for constants.
IsKeywordLikeName(const std::string & s)183 bool IsKeywordLikeName(const std::string& s) {
184   static const char* const keyword_like_constants[]{"True", "False", "TheHole",
185                                                     "Null", "Undefined"};
186 
187   return std::find(std::begin(keyword_like_constants),
188                    std::end(keyword_like_constants),
189                    s) != std::end(keyword_like_constants);
190 }
191 
192 // Untagged/MachineTypes like 'int32', 'intptr' etc. follow a 'all-lowercase'
193 // naming convention and are those exempt from the normal type convention.
IsMachineType(const std::string & s)194 bool IsMachineType(const std::string& s) {
195   static const char* const machine_types[]{
196       VOID_TYPE_STRING,    NEVER_TYPE_STRING,
197       INT8_TYPE_STRING,    UINT8_TYPE_STRING,
198       INT16_TYPE_STRING,   UINT16_TYPE_STRING,
199       INT31_TYPE_STRING,   UINT31_TYPE_STRING,
200       INT32_TYPE_STRING,   UINT32_TYPE_STRING,
201       INT64_TYPE_STRING,   INTPTR_TYPE_STRING,
202       UINTPTR_TYPE_STRING, FLOAT32_TYPE_STRING,
203       FLOAT64_TYPE_STRING, FLOAT64_OR_HOLE_TYPE_STRING,
204       BOOL_TYPE_STRING,    "string",
205       BINT_TYPE_STRING,    CHAR8_TYPE_STRING,
206       CHAR16_TYPE_STRING};
207 
208   return std::find(std::begin(machine_types), std::end(machine_types), s) !=
209          std::end(machine_types);
210 }
211 
212 }  // namespace
213 
IsLowerCamelCase(const std::string & s)214 bool IsLowerCamelCase(const std::string& s) {
215   if (s.empty()) return false;
216   size_t start = 0;
217   if (s[0] == '_') start = 1;
218   return islower(s[start]) && !ContainsUnderscore(s.substr(start));
219 }
220 
IsUpperCamelCase(const std::string & s)221 bool IsUpperCamelCase(const std::string& s) {
222   if (s.empty()) return false;
223   size_t start = 0;
224   if (s[0] == '_') start = 1;
225   return isupper(s[start]);
226 }
227 
IsSnakeCase(const std::string & s)228 bool IsSnakeCase(const std::string& s) {
229   if (s.empty()) return false;
230   return !ContainsUpperCase(s);
231 }
232 
IsValidNamespaceConstName(const std::string & s)233 bool IsValidNamespaceConstName(const std::string& s) {
234   if (s.empty()) return false;
235   if (IsKeywordLikeName(s)) return true;
236 
237   return s[0] == 'k' && IsUpperCamelCase(s.substr(1));
238 }
239 
IsValidTypeName(const std::string & s)240 bool IsValidTypeName(const std::string& s) {
241   if (s.empty()) return false;
242   if (IsMachineType(s)) return true;
243 
244   return IsUpperCamelCase(s);
245 }
246 
CapifyStringWithUnderscores(const std::string & camellified_string)247 std::string CapifyStringWithUnderscores(const std::string& camellified_string) {
248   // Special case: JSAbc yields JS_ABC, not JSABC, for any Abc.
249   size_t js_position = camellified_string.find("JS");
250 
251   std::string result;
252   bool previousWasLowerOrDigit = false;
253   for (size_t index = 0; index < camellified_string.size(); ++index) {
254     char current = camellified_string[index];
255     if ((previousWasLowerOrDigit && isupper(current)) ||
256         (js_position != std::string::npos &&
257          index == js_position + strlen("JS"))) {
258       result += "_";
259     }
260     if (current == '.' || current == '-') {
261       result += "_";
262       previousWasLowerOrDigit = false;
263       continue;
264     }
265     result += toupper(current);
266     previousWasLowerOrDigit = islower(current) || isdigit(current);
267   }
268   return result;
269 }
270 
CamelifyString(const std::string & underscore_string)271 std::string CamelifyString(const std::string& underscore_string) {
272   std::string result;
273   bool word_beginning = true;
274   for (auto current : underscore_string) {
275     if (current == '_' || current == '-') {
276       word_beginning = true;
277       continue;
278     }
279     if (word_beginning) {
280       current = toupper(current);
281     }
282     result += current;
283     word_beginning = false;
284   }
285   return result;
286 }
287 
SnakeifyString(const std::string & camel_string)288 std::string SnakeifyString(const std::string& camel_string) {
289   std::string result;
290   bool previousWasLower = false;
291   for (auto current : camel_string) {
292     if (previousWasLower && isupper(current)) {
293       result += "_";
294     }
295     result += tolower(current);
296     previousWasLower = (islower(current));
297   }
298   return result;
299 }
300 
DashifyString(const std::string & underscore_string)301 std::string DashifyString(const std::string& underscore_string) {
302   std::string result = underscore_string;
303   std::replace(result.begin(), result.end(), '_', '-');
304   return result;
305 }
306 
UnderlinifyPath(std::string path)307 std::string UnderlinifyPath(std::string path) {
308   std::replace(path.begin(), path.end(), '-', '_');
309   std::replace(path.begin(), path.end(), '/', '_');
310   std::replace(path.begin(), path.end(), '\\', '_');
311   std::replace(path.begin(), path.end(), '.', '_');
312   transform(path.begin(), path.end(), path.begin(), ::toupper);
313   return path;
314 }
315 
ReplaceFileContentsIfDifferent(const std::string & file_path,const std::string & contents)316 void ReplaceFileContentsIfDifferent(const std::string& file_path,
317                                     const std::string& contents) {
318   std::ifstream old_contents_stream(file_path.c_str());
319   std::string old_contents;
320   if (old_contents_stream.good()) {
321     std::istreambuf_iterator<char> eos;
322     old_contents =
323         std::string(std::istreambuf_iterator<char>(old_contents_stream), eos);
324     old_contents_stream.close();
325   }
326   if (old_contents.length() == 0 || old_contents != contents) {
327     std::ofstream new_contents_stream;
328     new_contents_stream.open(file_path.c_str());
329     new_contents_stream << contents;
330     new_contents_stream.close();
331   }
332 }
333 
IfDefScope(std::ostream & os,std::string d)334 IfDefScope::IfDefScope(std::ostream& os, std::string d)
335     : os_(os), d_(std::move(d)) {
336   os_ << "#ifdef " << d_ << "\n";
337 }
~IfDefScope()338 IfDefScope::~IfDefScope() { os_ << "#endif  // " << d_ << "\n"; }
339 
NamespaceScope(std::ostream & os,std::initializer_list<std::string> namespaces)340 NamespaceScope::NamespaceScope(std::ostream& os,
341                                std::initializer_list<std::string> namespaces)
342     : os_(os), d_(std::move(namespaces)) {
343   for (const std::string& s : d_) {
344     os_ << "namespace " << s << " {\n";
345   }
346 }
~NamespaceScope()347 NamespaceScope::~NamespaceScope() {
348   for (auto i = d_.rbegin(); i != d_.rend(); ++i) {
349     os_ << "}  // namespace " << *i << "\n";
350   }
351 }
352 
IncludeGuardScope(std::ostream & os,std::string file_name)353 IncludeGuardScope::IncludeGuardScope(std::ostream& os, std::string file_name)
354     : os_(os),
355       d_("V8_GEN_TORQUE_GENERATED_" + CapifyStringWithUnderscores(file_name) +
356          "_") {
357   os_ << "#ifndef " << d_ << "\n";
358   os_ << "#define " << d_ << "\n\n";
359 }
~IncludeGuardScope()360 IncludeGuardScope::~IncludeGuardScope() { os_ << "#endif  // " << d_ << "\n"; }
361 
IncludeObjectMacrosScope(std::ostream & os)362 IncludeObjectMacrosScope::IncludeObjectMacrosScope(std::ostream& os) : os_(os) {
363   os_ << "\n// Has to be the last include (doesn't have include guards):\n"
364          "#include \"src/objects/object-macros.h\"\n";
365 }
~IncludeObjectMacrosScope()366 IncludeObjectMacrosScope::~IncludeObjectMacrosScope() {
367   os_ << "\n#include \"src/objects/object-macros-undef.h\"\n";
368 }
369 
AlignmentLog2() const370 size_t ResidueClass::AlignmentLog2() const {
371   if (value_ == 0) return modulus_log_2_;
372   return base::bits::CountTrailingZeros(value_);
373 }
374 
375 const size_t ResidueClass::kMaxModulusLog2;
376 
operator <<(std::ostream & os,const ResidueClass & a)377 std::ostream& operator<<(std::ostream& os, const ResidueClass& a) {
378   if (a.SingleValue().has_value()) return os << *a.SingleValue();
379   return os << "[" << a.value_ << " mod 2^" << a.modulus_log_2_ << "]";
380 }
381 
382 }  // namespace torque
383 }  // namespace internal
384 }  // namespace v8
385