utils.cc 11.2 KB
Newer Older
1 2 3 4
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Marja Hölttä's avatar
Marja Hölttä committed
5
#include <algorithm>
6 7 8 9
#include <fstream>
#include <iostream>
#include <string>

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 16 17 18 19 20
#include "src/torque/utils.h"

namespace v8 {
namespace internal {
namespace torque {

21
DEFINE_CONTEXTUAL_VARIABLE(TorqueMessages)
22

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
std::string StringLiteralUnquote(const std::string& s) {
  DCHECK(('"' == s.front() && '"' == s.back()) ||
         ('\'' == s.front() && '\'' == s.back()));
  std::stringstream result;
  for (size_t i = 1; i < s.length() - 1; ++i) {
    if (s[i] == '\\') {
      switch (s[++i]) {
        case 'n':
          result << '\n';
          break;
        case 'r':
          result << '\r';
          break;
        case 't':
          result << '\t';
          break;
        case '\'':
        case '"':
        case '\\':
          result << s[i];
          break;
        default:
          UNREACHABLE();
      }
    } else {
      result << s[i];
    }
  }
  return result.str();
}

std::string StringLiteralQuote(const std::string& s) {
  std::stringstream result;
  result << '"';
57
  for (size_t i = 0; i < s.length(); ++i) {
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    switch (s[i]) {
      case '\n':
        result << "\\n";
        break;
      case '\r':
        result << "\\r";
        break;
      case '\t':
        result << "\\t";
        break;
      case '"':
      case '\\':
        result << "\\" << s[i];
        break;
      default:
        result << s[i];
    }
  }
  result << '"';
  return result.str();
}

80 81 82
#ifdef V8_OS_WIN
static const char kFileUriPrefix[] = "file:///";
#else
83
static const char kFileUriPrefix[] = "file://";
84
#endif
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
static const int kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1;

static int HexCharToInt(unsigned char c) {
  if (isdigit(c)) return c - '0';
  if (isupper(c)) return c - 'A' + 10;
  DCHECK(islower(c));
  return c - 'a' + 10;
}

base::Optional<std::string> FileUriDecode(const std::string& uri) {
  // Abort decoding of URIs that don't start with "file://".
  if (uri.rfind(kFileUriPrefix) != 0) return base::nullopt;

  const std::string path = uri.substr(kFileUriPrefixLength);
  std::ostringstream decoded;

  for (auto iter = path.begin(), end = path.end(); iter != end; ++iter) {
    std::string::value_type c = (*iter);

    // Normal characters are appended.
    if (c != '%') {
      decoded << c;
      continue;
    }

    // If '%' is not followed by at least two hex digits, we abort.
    if (std::distance(iter, end) <= 2) return base::nullopt;

    unsigned char first = (*++iter);
    unsigned char second = (*++iter);
    if (!isxdigit(first) || !isxdigit(second)) return base::nullopt;

    // An escaped hex value needs converting.
    unsigned char value = HexCharToInt(first) * 16 + HexCharToInt(second);
    decoded << value;
  }

  return decoded.str();
}

125 126 127 128
std::string CurrentPositionAsString() {
  return PositionAsString(CurrentSourcePosition::Get());
}

129 130 131 132 133 134 135
MessageBuilder::MessageBuilder(const std::string& message,
                               TorqueMessage::Kind kind) {
  base::Optional<SourcePosition> position;
  if (CurrentSourcePosition::HasScope()) {
    position = CurrentSourcePosition::Get();
  }
  message_ = TorqueMessage{message, position, kind};
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
  if (CurrentScope::HasScope()) {
    // Traverse the parent scopes to find one that was created to represent a
    // specialization of something generic. If we find one, then log it and
    // continue walking the scope tree of the code that requested that
    // specialization. This allows us to collect the stack of locations that
    // caused a specialization.
    Scope* scope = CurrentScope::Get();
    while (scope) {
      SpecializationRequester requester = scope->GetSpecializationRequester();
      if (!requester.IsNone()) {
        extra_messages_.push_back(
            {"Note: in specialization " + requester.name + " requested here",
             requester.position, kind});
        scope = requester.scope;
      } else {
        scope = scope->ParentScope();
      }
    }
  }
155 156
}

157 158
void MessageBuilder::Report() const {
  TorqueMessages::Get().push_back(message_);
159 160 161
  for (const auto& message : extra_messages_) {
    TorqueMessages::Get().push_back(message);
  }
162 163 164 165
}

[[noreturn]] void MessageBuilder::Throw() const {
  throw TorqueAbortCompilation{};
166 167
}

168 169 170 171 172 173 174
namespace {

bool ContainsUnderscore(const std::string& s) {
  if (s.empty()) return false;
  return s.find("_") != std::string::npos;
}

175 176 177 178 179
bool ContainsUpperCase(const std::string& s) {
  if (s.empty()) return false;
  return std::any_of(s.begin(), s.end(), [](char c) { return isupper(c); });
}

180
// Torque has some namespace constants that are used like language level
181 182 183
// keywords, e.g.: 'True', 'Undefined', etc.
// These do not need to follow the default naming convention for constants.
bool IsKeywordLikeName(const std::string& s) {
184
  static const char* const keyword_like_constants[]{"True", "False", "TheHole",
185
                                                    "Null", "Undefined"};
186

187 188 189
  return std::find(std::begin(keyword_like_constants),
                   std::end(keyword_like_constants),
                   s) != std::end(keyword_like_constants);
190 191
}

192 193 194
// Untagged/MachineTypes like 'int32', 'intptr' etc. follow a 'all-lowercase'
// naming convention and are those exempt from the normal type convention.
bool IsMachineType(const std::string& s) {
195
  static const char* const machine_types[]{
196 197 198 199 200 201 202 203 204 205 206
      VOID_TYPE_STRING,    NEVER_TYPE_STRING,
      INT8_TYPE_STRING,    UINT8_TYPE_STRING,
      INT16_TYPE_STRING,   UINT16_TYPE_STRING,
      INT31_TYPE_STRING,   UINT31_TYPE_STRING,
      INT32_TYPE_STRING,   UINT32_TYPE_STRING,
      INT64_TYPE_STRING,   INTPTR_TYPE_STRING,
      UINTPTR_TYPE_STRING, FLOAT32_TYPE_STRING,
      FLOAT64_TYPE_STRING, FLOAT64_OR_HOLE_TYPE_STRING,
      BOOL_TYPE_STRING,    "string",
      BINT_TYPE_STRING,    CHAR8_TYPE_STRING,
      CHAR16_TYPE_STRING};
207

208 209
  return std::find(std::begin(machine_types), std::end(machine_types), s) !=
         std::end(machine_types);
210 211
}

212 213 214 215
}  // namespace

bool IsLowerCamelCase(const std::string& s) {
  if (s.empty()) return false;
216 217 218
  size_t start = 0;
  if (s[0] == '_') start = 1;
  return islower(s[start]) && !ContainsUnderscore(s.substr(start));
219 220 221 222
}

bool IsUpperCamelCase(const std::string& s) {
  if (s.empty()) return false;
223 224
  size_t start = 0;
  if (s[0] == '_') start = 1;
225
  return isupper(s[start]);
226 227
}

228 229 230 231 232
bool IsSnakeCase(const std::string& s) {
  if (s.empty()) return false;
  return !ContainsUpperCase(s);
}

233
bool IsValidNamespaceConstName(const std::string& s) {
234 235 236 237 238 239
  if (s.empty()) return false;
  if (IsKeywordLikeName(s)) return true;

  return s[0] == 'k' && IsUpperCamelCase(s.substr(1));
}

240 241 242 243 244 245 246
bool IsValidTypeName(const std::string& s) {
  if (s.empty()) return false;
  if (IsMachineType(s)) return true;

  return IsUpperCamelCase(s);
}

247
std::string CapifyStringWithUnderscores(const std::string& camellified_string) {
248 249 250
  // Special case: JSAbc yields JS_ABC, not JSABC, for any Abc.
  size_t js_position = camellified_string.find("JS");

251
  std::string result;
252 253 254 255 256 257
  bool previousWasLowerOrDigit = false;
  for (size_t index = 0; index < camellified_string.size(); ++index) {
    char current = camellified_string[index];
    if ((previousWasLowerOrDigit && isupper(current)) ||
        (js_position != std::string::npos &&
         index == js_position + strlen("JS"))) {
258 259
      result += "_";
    }
260 261
    if (current == '.' || current == '-') {
      result += "_";
262
      previousWasLowerOrDigit = false;
263 264
      continue;
    }
265
    result += toupper(current);
266
    previousWasLowerOrDigit = islower(current) || isdigit(current);
267 268 269 270
  }
  return result;
}

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
std::string CamelifyString(const std::string& underscore_string) {
  std::string result;
  bool word_beginning = true;
  for (auto current : underscore_string) {
    if (current == '_' || current == '-') {
      word_beginning = true;
      continue;
    }
    if (word_beginning) {
      current = toupper(current);
    }
    result += current;
    word_beginning = false;
  }
  return result;
}

288 289 290 291 292 293 294 295 296 297 298 299 300
std::string SnakeifyString(const std::string& camel_string) {
  std::string result;
  bool previousWasLower = false;
  for (auto current : camel_string) {
    if (previousWasLower && isupper(current)) {
      result += "_";
    }
    result += tolower(current);
    previousWasLower = (islower(current));
  }
  return result;
}

301 302 303 304 305 306
std::string DashifyString(const std::string& underscore_string) {
  std::string result = underscore_string;
  std::replace(result.begin(), result.end(), '_', '-');
  return result;
}

307 308 309 310 311 312 313 314 315
std::string UnderlinifyPath(std::string path) {
  std::replace(path.begin(), path.end(), '-', '_');
  std::replace(path.begin(), path.end(), '/', '_');
  std::replace(path.begin(), path.end(), '\\', '_');
  std::replace(path.begin(), path.end(), '.', '_');
  transform(path.begin(), path.end(), path.begin(), ::toupper);
  return path;
}

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
void ReplaceFileContentsIfDifferent(const std::string& file_path,
                                    const std::string& contents) {
  std::ifstream old_contents_stream(file_path.c_str());
  std::string old_contents;
  if (old_contents_stream.good()) {
    std::istreambuf_iterator<char> eos;
    old_contents =
        std::string(std::istreambuf_iterator<char>(old_contents_stream), eos);
    old_contents_stream.close();
  }
  if (old_contents.length() == 0 || old_contents != contents) {
    std::ofstream new_contents_stream;
    new_contents_stream.open(file_path.c_str());
    new_contents_stream << contents;
    new_contents_stream.close();
  }
}

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
IfDefScope::IfDefScope(std::ostream& os, std::string d)
    : os_(os), d_(std::move(d)) {
  os_ << "#ifdef " << d_ << "\n";
}
IfDefScope::~IfDefScope() { os_ << "#endif  // " << d_ << "\n"; }

NamespaceScope::NamespaceScope(std::ostream& os,
                               std::initializer_list<std::string> namespaces)
    : os_(os), d_(std::move(namespaces)) {
  for (const std::string& s : d_) {
    os_ << "namespace " << s << " {\n";
  }
}
NamespaceScope::~NamespaceScope() {
  for (auto i = d_.rbegin(); i != d_.rend(); ++i) {
    os_ << "}  // namespace " << *i << "\n";
  }
}

IncludeGuardScope::IncludeGuardScope(std::ostream& os, std::string file_name)
    : os_(os),
      d_("V8_GEN_TORQUE_GENERATED_" + CapifyStringWithUnderscores(file_name) +
         "_") {
  os_ << "#ifndef " << d_ << "\n";
  os_ << "#define " << d_ << "\n\n";
}
IncludeGuardScope::~IncludeGuardScope() { os_ << "#endif  // " << d_ << "\n"; }

IncludeObjectMacrosScope::IncludeObjectMacrosScope(std::ostream& os) : os_(os) {
  os_ << "\n// Has to be the last include (doesn't have include guards):\n"
         "#include \"src/objects/object-macros.h\"\n";
}
IncludeObjectMacrosScope::~IncludeObjectMacrosScope() {
  os_ << "\n#include \"src/objects/object-macros-undef.h\"\n";
}

370 371 372 373 374 375 376 377 378 379 380 381
size_t ResidueClass::AlignmentLog2() const {
  if (value_ == 0) return modulus_log_2_;
  return base::bits::CountTrailingZeros(value_);
}

const size_t ResidueClass::kMaxModulusLog2;

std::ostream& operator<<(std::ostream& os, const ResidueClass& a) {
  if (a.SingleValue().has_value()) return os << *a.SingleValue();
  return os << "[" << a.value_ << " mod 2^" << a.modulus_log_2_ << "]";
}

382 383 384
}  // namespace torque
}  // namespace internal
}  // namespace v8