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