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

5 6
#include "src/flags.h"

7 8 9
#include <cctype>
#include <cstdlib>
#include <sstream>
10

11
#include "src/allocation.h"
12
#include "src/assembler.h"
13
#include "src/base/functional.h"
14
#include "src/base/platform/platform.h"
15
#include "src/list-inl.h"
16
#include "src/ostreams.h"
17
#include "src/utils.h"
18

19 20
namespace v8 {
namespace internal {
21

22 23
// Define all of our flags.
#define FLAG_MODE_DEFINE
24
#include "src/flag-definitions.h"  // NOLINT(build/include)
25

26 27
// Define all of our flags default values.
#define FLAG_MODE_DEFINE_DEFAULTS
28
#include "src/flag-definitions.h"  // NOLINT(build/include)
29 30 31 32 33 34 35

namespace {

// This structure represents a single entry in the flag system, with a pointer
// to the actual flag, default value, comment, etc.  This is designed to be POD
// initialized as to avoid requiring static constructors.
struct Flag {
36 37
  enum FlagType { TYPE_BOOL, TYPE_MAYBE_BOOL, TYPE_INT, TYPE_FLOAT,
                  TYPE_STRING, TYPE_ARGS };
38 39 40 41 42 43

  FlagType type_;           // What type of flag, bool, int, or string.
  const char* name_;        // Name of the flag, ex "my_flag".
  void* valptr_;            // Pointer to the global flag variable.
  const void* defptr_;      // Pointer to the default value.
  const char* cmt_;         // A comment about the flags purpose.
44
  bool owns_ptr_;           // Does the flag own its string value?
45 46

  FlagType type() const { return type_; }
47

48
  const char* name() const { return name_; }
49

50 51 52
  const char* comment() const { return cmt_; }

  bool* bool_variable() const {
53
    DCHECK(type_ == TYPE_BOOL);
54
    return reinterpret_cast<bool*>(valptr_);
55 56
  }

57
  MaybeBoolFlag* maybe_bool_variable() const {
58
    DCHECK(type_ == TYPE_MAYBE_BOOL);
59 60 61
    return reinterpret_cast<MaybeBoolFlag*>(valptr_);
  }

62
  int* int_variable() const {
63
    DCHECK(type_ == TYPE_INT);
64 65
    return reinterpret_cast<int*>(valptr_);
  }
66

67
  double* float_variable() const {
68
    DCHECK(type_ == TYPE_FLOAT);
69
    return reinterpret_cast<double*>(valptr_);
70 71
  }

72
  const char* string_value() const {
73
    DCHECK(type_ == TYPE_STRING);
74 75 76
    return *reinterpret_cast<const char**>(valptr_);
  }

77
  void set_string_value(const char* value, bool owns_ptr) {
78
    DCHECK(type_ == TYPE_STRING);
79
    const char** ptr = reinterpret_cast<const char**>(valptr_);
80 81 82
    if (owns_ptr_ && *ptr != NULL) DeleteArray(*ptr);
    *ptr = value;
    owns_ptr_ = owns_ptr;
83
  }
84

85
  JSArguments* args_variable() const {
86
    DCHECK(type_ == TYPE_ARGS);
87 88 89
    return reinterpret_cast<JSArguments*>(valptr_);
  }

90
  bool bool_default() const {
91
    DCHECK(type_ == TYPE_BOOL);
92 93
    return *reinterpret_cast<const bool*>(defptr_);
  }
94

95
  int int_default() const {
96
    DCHECK(type_ == TYPE_INT);
97 98
    return *reinterpret_cast<const int*>(defptr_);
  }
99

100
  double float_default() const {
101
    DCHECK(type_ == TYPE_FLOAT);
102 103
    return *reinterpret_cast<const double*>(defptr_);
  }
104

105
  const char* string_default() const {
106
    DCHECK(type_ == TYPE_STRING);
107
    return *reinterpret_cast<const char* const *>(defptr_);
108 109
  }

110
  JSArguments args_default() const {
111
    DCHECK(type_ == TYPE_ARGS);
112 113 114
    return *reinterpret_cast<const JSArguments*>(defptr_);
  }

115 116 117
  // Compare this flag's current value against the default.
  bool IsDefault() const {
    switch (type_) {
118 119
      case TYPE_BOOL:
        return *bool_variable() == bool_default();
120 121
      case TYPE_MAYBE_BOOL:
        return maybe_bool_variable()->has_value == false;
122 123 124 125
      case TYPE_INT:
        return *int_variable() == int_default();
      case TYPE_FLOAT:
        return *float_variable() == float_default();
126
      case TYPE_STRING: {
127
        const char* str1 = string_value();
128 129 130 131
        const char* str2 = string_default();
        if (str2 == NULL) return str1 == NULL;
        if (str1 == NULL) return str2 == NULL;
        return strcmp(str1, str2) == 0;
132 133
      }
      case TYPE_ARGS:
134
        return args_variable()->argc == 0;
135
    }
136 137
    UNREACHABLE();
    return true;
138
  }
139

140 141 142
  // Set a flag back to it's default value.
  void Reset() {
    switch (type_) {
143 144 145
      case TYPE_BOOL:
        *bool_variable() = bool_default();
        break;
146 147 148
      case TYPE_MAYBE_BOOL:
        *maybe_bool_variable() = MaybeBoolFlag::Create(false, false);
        break;
149 150 151 152 153 154 155
      case TYPE_INT:
        *int_variable() = int_default();
        break;
      case TYPE_FLOAT:
        *float_variable() = float_default();
        break;
      case TYPE_STRING:
156
        set_string_value(string_default(), false);
157
        break;
158 159 160
      case TYPE_ARGS:
        *args_variable() = args_default();
        break;
161
    }
162
  }
163 164 165 166
};

Flag flags[] = {
#define FLAG_MODE_META
167
#include "src/flag-definitions.h"  // NOLINT(build/include)
168 169 170 171 172
};

const size_t num_flags = sizeof(flags) / sizeof(*flags);

}  // namespace
173 174


175
static const char* Type2String(Flag::FlagType type) {
176
  switch (type) {
177
    case Flag::TYPE_BOOL: return "bool";
178
    case Flag::TYPE_MAYBE_BOOL: return "maybe_bool";
179 180 181
    case Flag::TYPE_INT: return "int";
    case Flag::TYPE_FLOAT: return "float";
    case Flag::TYPE_STRING: return "string";
182
    case Flag::TYPE_ARGS: return "arguments";
183 184 185 186 187 188
  }
  UNREACHABLE();
  return NULL;
}


189
std::ostream& operator<<(std::ostream& os, const Flag& flag) {  // NOLINT
190
  switch (flag.type()) {
191
    case Flag::TYPE_BOOL:
192
      os << (*flag.bool_variable() ? "true" : "false");
193
      break;
194
    case Flag::TYPE_MAYBE_BOOL:
195 196 197
      os << (flag.maybe_bool_variable()->has_value
                 ? (flag.maybe_bool_variable()->value ? "true" : "false")
                 : "unset");
198
      break;
199
    case Flag::TYPE_INT:
200
      os << *flag.int_variable();
201
      break;
202
    case Flag::TYPE_FLOAT:
203
      os << *flag.float_variable();
204
      break;
205
    case Flag::TYPE_STRING: {
206 207
      const char* str = flag.string_value();
      os << (str ? str : "NULL");
208 209 210
      break;
    }
    case Flag::TYPE_ARGS: {
211
      JSArguments args = *flag.args_variable();
212
      if (args.argc > 0) {
213
        os << args[0];
214
        for (int i = 1; i < args.argc; i++) {
215
          os << args[i];
216
        }
217 218
      }
      break;
219
    }
220
  }
221
  return os;
222 223 224
}


225
// static
226 227 228
List<const char*>* FlagList::argv() {
  List<const char*>* args = new List<const char*>(8);
  Flag* args_flag = NULL;
229 230
  for (size_t i = 0; i < num_flags; ++i) {
    Flag* f = &flags[i];
231
    if (!f->IsDefault()) {
232
      if (f->type() == Flag::TYPE_ARGS) {
233
        DCHECK(args_flag == NULL);
234 235 236
        args_flag = f;  // Must be last in arguments.
        continue;
      }
237 238
      {
        bool disabled = f->type() == Flag::TYPE_BOOL && !*f->bool_variable();
239
        std::ostringstream os;
240
        os << (disabled ? "--no" : "--") << f->name();
241
        args->Add(StrDup(os.str().c_str()));
242
      }
243
      if (f->type() != Flag::TYPE_BOOL) {
244
        std::ostringstream os;
245
        os << *f;
246
        args->Add(StrDup(os.str().c_str()));
247 248 249
      }
    }
  }
250
  if (args_flag != NULL) {
251
    std::ostringstream os;
252
    os << "--" << args_flag->name();
253
    args->Add(StrDup(os.str().c_str()));
254
    JSArguments jsargs = *args_flag->args_variable();
255
    for (int j = 0; j < jsargs.argc; j++) {
256
      args->Add(StrDup(jsargs[j]));
257 258
    }
  }
259
  return args;
260 261 262
}


263 264 265 266 267
inline char NormalizeChar(char ch) {
  return ch == '_' ? '-' : ch;
}


268 269 270 271 272 273 274 275 276 277
// Helper function to parse flags: Takes an argument arg and splits it into
// a flag name and flag value (or NULL if they are missing). is_bool is set
// if the arg started with "-no" or "--no". The buffer may be used to NUL-
// terminate the name, it must be large enough to hold any possible name.
static void SplitArgument(const char* arg,
                          char* buffer,
                          int buffer_size,
                          const char** name,
                          const char** value,
                          bool* is_bool) {
278 279 280 281
  *name = NULL;
  *value = NULL;
  *is_bool = false;

282
  if (arg != NULL && *arg == '-') {
283 284
    // find the begin of the flag name
    arg++;  // remove 1st '-'
285
    if (*arg == '-') {
286
      arg++;  // remove 2nd '-'
287 288 289 290 291 292
      if (arg[0] == '\0') {
        const char* kJSArgumentsFlagName = "js_arguments";
        *name = kJSArgumentsFlagName;
        return;
      }
    }
293 294
    if (arg[0] == 'n' && arg[1] == 'o') {
      arg += 2;  // remove "no"
295
      if (NormalizeChar(arg[0]) == '-') arg++;  // remove dash after "no".
296 297 298 299 300 301 302 303 304 305 306
      *is_bool = true;
    }
    *name = arg;

    // find the end of the flag name
    while (*arg != '\0' && *arg != '=')
      arg++;

    // get the value if any
    if (*arg == '=') {
      // make a copy so we can NUL-terminate flag name
307 308
      size_t n = arg - *name;
      CHECK(n < static_cast<size_t>(buffer_size));  // buffer is too small
309
      MemCopy(buffer, *name, n);
310 311 312 313 314 315 316 317 318
      buffer[n] = '\0';
      *name = buffer;
      // get the value
      *value = arg + 1;
    }
  }
}


319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
static bool EqualNames(const char* a, const char* b) {
  for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
    if (a[i] == '\0') {
      return true;
    }
  }
  return false;
}


static Flag* FindFlag(const char* name) {
  for (size_t i = 0; i < num_flags; ++i) {
    if (EqualNames(name, flags[i].name()))
      return &flags[i];
  }
  return NULL;
}


// static
339 340
int FlagList::SetFlagsFromCommandLine(int* argc,
                                      char** argv,
341
                                      bool remove_flags) {
342
  int return_code = 0;
343 344 345 346 347 348 349 350 351 352 353 354 355 356
  // parse arguments
  for (int i = 1; i < *argc;) {
    int j = i;  // j > 0
    const char* arg = argv[i++];

    // split arg into flag components
    char buffer[1*KB];
    const char* name;
    const char* value;
    bool is_bool;
    SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);

    if (name != NULL) {
      // lookup the flag
357
      Flag* flag = FindFlag(name);
358
      if (flag == NULL) {
359 360 361 362 363 364 365
        if (remove_flags) {
          // We don't recognize this flag but since we're removing
          // the flags we recognize we assume that the remaining flags
          // will be processed somewhere else so this flag might make
          // sense there.
          continue;
        } else {
366 367
          PrintF(stderr, "Error: unrecognized flag %s\n"
                 "Try --help for options\n", arg);
368 369
          return_code = j;
          break;
370
        }
371 372 373
      }

      // if we still need a flag value, use the next argument if available
374
      if (flag->type() != Flag::TYPE_BOOL &&
375
          flag->type() != Flag::TYPE_MAYBE_BOOL &&
376 377
          flag->type() != Flag::TYPE_ARGS &&
          value == NULL) {
378 379
        if (i < *argc) {
          value = argv[i++];
380 381
        }
        if (!value) {
382 383 384
          PrintF(stderr, "Error: missing value for flag %s of type %s\n"
                 "Try --help for options\n",
                 arg, Type2String(flag->type()));
385 386
          return_code = j;
          break;
387 388 389 390 391 392
        }
      }

      // set the flag
      char* endp = const_cast<char*>("");  // *endp is only read
      switch (flag->type()) {
393
        case Flag::TYPE_BOOL:
394 395
          *flag->bool_variable() = !is_bool;
          break;
396 397 398
        case Flag::TYPE_MAYBE_BOOL:
          *flag->maybe_bool_variable() = MaybeBoolFlag::Create(true, !is_bool);
          break;
399
        case Flag::TYPE_INT:
400
          *flag->int_variable() = static_cast<int>(strtol(value, &endp, 10));
401
          break;
402
        case Flag::TYPE_FLOAT:
403 404
          *flag->float_variable() = strtod(value, &endp);
          break;
405
        case Flag::TYPE_STRING:
406
          flag->set_string_value(value ? StrDup(value) : NULL, true);
407
          break;
408 409 410 411 412
        case Flag::TYPE_ARGS: {
          int start_pos = (value == NULL) ? i : i - 1;
          int js_argc = *argc - start_pos;
          const char** js_argv = NewArray<const char*>(js_argc);
          if (value != NULL) {
413
            js_argv[0] = StrDup(value);
414 415
          }
          for (int k = i; k < *argc; k++) {
416
            js_argv[k - start_pos] = StrDup(argv[k]);
417
          }
418
          *flag->args_variable() = JSArguments::Create(js_argc, js_argv);
419 420 421
          i = *argc;  // Consume all arguments
          break;
        }
422 423 424
      }

      // handle errors
425 426 427
      bool is_bool_type = flag->type() == Flag::TYPE_BOOL ||
          flag->type() == Flag::TYPE_MAYBE_BOOL;
      if ((is_bool_type && value != NULL) || (!is_bool_type && is_bool) ||
428
          *endp != '\0') {
429 430 431
        PrintF(stderr, "Error: illegal value for flag %s of type %s\n"
               "Try --help for options\n",
               arg, Type2String(flag->type()));
432 433
        return_code = j;
        break;
434 435 436
      }

      // remove the flag & value from the command
437 438
      if (remove_flags) {
        while (j < i) {
439
          argv[j++] = NULL;
440 441
        }
      }
442 443 444 445 446 447 448 449 450 451 452 453 454
    }
  }

  // shrink the argument list
  if (remove_flags) {
    int j = 1;
    for (int i = 1; i < *argc; i++) {
      if (argv[i] != NULL)
        argv[j++] = argv[i];
    }
    *argc = j;
  }

455 456
  if (FLAG_help) {
    PrintHelp();
457
    exit(0);
458
  }
459
  // parsed all flags successfully
460
  return return_code;
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
}


static char* SkipWhiteSpace(char* p) {
  while (*p != '\0' && isspace(*p) != 0) p++;
  return p;
}


static char* SkipBlackSpace(char* p) {
  while (*p != '\0' && isspace(*p) == 0) p++;
  return p;
}


476
// static
477 478
int FlagList::SetFlagsFromString(const char* str, int len) {
  // make a 0-terminated copy of str
479
  ScopedVector<char> copy0(len + 1);
480
  MemCopy(copy0.start(), str, len);
481 482 483
  copy0[len] = '\0';

  // strip leading white space
484
  char* copy = SkipWhiteSpace(copy0.start());
485 486 487 488 489 490 491 492 493

  // count the number of 'arguments'
  int argc = 1;  // be compatible with SetFlagsFromCommandLine()
  for (char* p = copy; *p != '\0'; argc++) {
    p = SkipBlackSpace(p);
    p = SkipWhiteSpace(p);
  }

  // allocate argument array
494
  ScopedVector<char*> argv(argc);
495 496 497 498 499 500 501 502 503 504 505

  // split the flags string into arguments
  argc = 1;  // be compatible with SetFlagsFromCommandLine()
  for (char* p = copy; *p != '\0'; argc++) {
    argv[argc] = p;
    p = SkipBlackSpace(p);
    if (*p != '\0') *p++ = '\0';  // 0-terminate argument
    p = SkipWhiteSpace(p);
  }

  // set the flags
506
  int result = SetFlagsFromCommandLine(&argc, argv.start(), false);
507 508 509 510 511

  return result;
}


512 513 514 515 516 517 518 519 520 521
// static
void FlagList::ResetAllFlags() {
  for (size_t i = 0; i < num_flags; ++i) {
    flags[i].Reset();
  }
}


// static
void FlagList::PrintHelp() {
522
  CpuFeatures::Probe(false);
523 524 525
  CpuFeatures::PrintTarget();
  CpuFeatures::PrintFeatures();

526 527 528 529 530 531 532 533 534 535 536 537 538 539
  OFStream os(stdout);
  os << "Usage:\n"
     << "  shell [options] -e string\n"
     << "    execute string in V8\n"
     << "  shell [options] file1 file2 ... filek\n"
     << "    run JavaScript scripts in file1, file2, ..., filek\n"
     << "  shell [options]\n"
     << "  shell [options] --shell [file1 file2 ... filek]\n"
     << "    run an interactive JavaScript shell\n"
     << "  d8 [options] file1 file2 ... filek\n"
     << "  d8 [options]\n"
     << "  d8 [options] --shell [file1 file2 ... filek]\n"
     << "    run the new debugging shell\n\n"
     << "Options:\n";
540 541
  for (size_t i = 0; i < num_flags; ++i) {
    Flag* f = &flags[i];
542 543 544
    os << "  --" << f->name() << " (" << f->comment() << ")\n"
       << "        type: " << Type2String(f->type()) << "  default: " << *f
       << "\n";
545
  }
546 547
}

548

549
static uint32_t flag_hash = 0;
550

551

552
void ComputeFlagListHash() {
553
  std::ostringstream modified_args_as_string;
554 555 556
#ifdef DEBUG
  modified_args_as_string << "debug";
#endif  // DEBUG
557 558 559
  for (size_t i = 0; i < num_flags; ++i) {
    Flag* current = &flags[i];
    if (!current->IsDefault()) {
560
      modified_args_as_string << i;
561 562 563 564
      modified_args_as_string << *current;
    }
  }
  std::string args(modified_args_as_string.str());
565
  flag_hash = static_cast<uint32_t>(
566 567
      base::hash_range(args.c_str(), args.c_str() + args.length()));
}
568 569 570 571 572


// static
void FlagList::EnforceFlagImplications() {
#define FLAG_MODE_DEFINE_IMPLICATIONS
573
#include "src/flag-definitions.h"  // NOLINT(build/include)
574 575 576 577 578 579
#undef FLAG_MODE_DEFINE_IMPLICATIONS
  ComputeFlagListHash();
}


uint32_t FlagList::Hash() { return flag_hash; }
580 581
}  // namespace internal
}  // namespace v8