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