flags.cc 15.7 KB
Newer Older
1
// Copyright 2006-2008 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <ctype.h>
#include <stdlib.h>

#include "v8.h"

#include "platform.h"
34
#include "smart-array-pointer.h"
35 36
#include "string-stream.h"

37

38 39
namespace v8 {
namespace internal {
40

41 42
// Define all of our flags.
#define FLAG_MODE_DEFINE
43
#include "flag-definitions.h"
44

45 46
// Define all of our flags default values.
#define FLAG_MODE_DEFINE_DEFAULTS
47
#include "flag-definitions.h"
48 49 50 51 52 53 54

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 {
55
  enum FlagType { TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_ARGS };
56 57 58 59 60 61

  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.
62
  bool owns_ptr_;           // Does the flag own its string value?
63 64

  FlagType type() const { return type_; }
65

66
  const char* name() const { return name_; }
67

68 69 70 71 72
  const char* comment() const { return cmt_; }

  bool* bool_variable() const {
    ASSERT(type_ == TYPE_BOOL);
    return reinterpret_cast<bool*>(valptr_);
73 74
  }

75 76 77 78
  int* int_variable() const {
    ASSERT(type_ == TYPE_INT);
    return reinterpret_cast<int*>(valptr_);
  }
79

80 81 82
  double* float_variable() const {
    ASSERT(type_ == TYPE_FLOAT);
    return reinterpret_cast<double*>(valptr_);
83 84
  }

85
  const char* string_value() const {
86
    ASSERT(type_ == TYPE_STRING);
87 88 89
    return *reinterpret_cast<const char**>(valptr_);
  }

90
  void set_string_value(const char* value, bool owns_ptr) {
91
    ASSERT(type_ == TYPE_STRING);
92
    const char** ptr = reinterpret_cast<const char**>(valptr_);
93 94 95
    if (owns_ptr_ && *ptr != NULL) DeleteArray(*ptr);
    *ptr = value;
    owns_ptr_ = owns_ptr;
96
  }
97

98 99 100 101 102
  JSArguments* args_variable() const {
    ASSERT(type_ == TYPE_ARGS);
    return reinterpret_cast<JSArguments*>(valptr_);
  }

103 104 105 106
  bool bool_default() const {
    ASSERT(type_ == TYPE_BOOL);
    return *reinterpret_cast<const bool*>(defptr_);
  }
107

108 109 110 111
  int int_default() const {
    ASSERT(type_ == TYPE_INT);
    return *reinterpret_cast<const int*>(defptr_);
  }
112

113 114 115 116
  double float_default() const {
    ASSERT(type_ == TYPE_FLOAT);
    return *reinterpret_cast<const double*>(defptr_);
  }
117

118 119 120
  const char* string_default() const {
    ASSERT(type_ == TYPE_STRING);
    return *reinterpret_cast<const char* const *>(defptr_);
121 122
  }

123 124 125 126 127
  JSArguments args_default() const {
    ASSERT(type_ == TYPE_ARGS);
    return *reinterpret_cast<const JSArguments*>(defptr_);
  }

128 129 130
  // Compare this flag's current value against the default.
  bool IsDefault() const {
    switch (type_) {
131 132 133 134 135 136
      case TYPE_BOOL:
        return *bool_variable() == bool_default();
      case TYPE_INT:
        return *int_variable() == int_default();
      case TYPE_FLOAT:
        return *float_variable() == float_default();
137
      case TYPE_STRING: {
138
        const char* str1 = string_value();
139 140 141 142
        const char* str2 = string_default();
        if (str2 == NULL) return str1 == NULL;
        if (str1 == NULL) return str2 == NULL;
        return strcmp(str1, str2) == 0;
143 144 145
      }
      case TYPE_ARGS:
        return args_variable()->argc() == 0;
146
    }
147 148
    UNREACHABLE();
    return true;
149
  }
150

151 152 153
  // Set a flag back to it's default value.
  void Reset() {
    switch (type_) {
154 155 156 157 158 159 160 161 162 163
      case TYPE_BOOL:
        *bool_variable() = bool_default();
        break;
      case TYPE_INT:
        *int_variable() = int_default();
        break;
      case TYPE_FLOAT:
        *float_variable() = float_default();
        break;
      case TYPE_STRING:
164
        set_string_value(string_default(), false);
165
        break;
166 167 168
      case TYPE_ARGS:
        *args_variable() = args_default();
        break;
169
    }
170
  }
171 172 173 174
};

Flag flags[] = {
#define FLAG_MODE_META
175
#include "flag-definitions.h"
176 177 178 179 180
};

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

}  // namespace
181 182


183
static const char* Type2String(Flag::FlagType type) {
184
  switch (type) {
185 186 187 188
    case Flag::TYPE_BOOL: return "bool";
    case Flag::TYPE_INT: return "int";
    case Flag::TYPE_FLOAT: return "float";
    case Flag::TYPE_STRING: return "string";
189
    case Flag::TYPE_ARGS: return "arguments";
190 191 192 193 194 195
  }
  UNREACHABLE();
  return NULL;
}


196
static SmartArrayPointer<const char> ToString(Flag* flag) {
197 198
  HeapStringAllocator string_allocator;
  StringStream buffer(&string_allocator);
199 200
  switch (flag->type()) {
    case Flag::TYPE_BOOL:
201
      buffer.Add("%s", (*flag->bool_variable() ? "true" : "false"));
202
      break;
203
    case Flag::TYPE_INT:
204
      buffer.Add("%d", *flag->int_variable());
205
      break;
206
    case Flag::TYPE_FLOAT:
207
      buffer.Add("%f", FmtElm(*flag->float_variable()));
208
      break;
209
    case Flag::TYPE_STRING: {
210
      const char* str = flag->string_value();
211 212 213 214 215 216 217 218 219 220
      buffer.Add("%s", str ? str : "NULL");
      break;
    }
    case Flag::TYPE_ARGS: {
      JSArguments args = *flag->args_variable();
      if (args.argc() > 0) {
        buffer.Add("%s",  args[0]);
        for (int i = 1; i < args.argc(); i++) {
          buffer.Add(" %s", args[i]);
        }
221 222
      }
      break;
223
    }
224
  }
225
  return buffer.ToCString();
226 227 228
}


229
// static
230 231 232
List<const char*>* FlagList::argv() {
  List<const char*>* args = new List<const char*>(8);
  Flag* args_flag = NULL;
233 234
  for (size_t i = 0; i < num_flags; ++i) {
    Flag* f = &flags[i];
235
    if (!f->IsDefault()) {
236 237 238 239 240 241 242
      if (f->type() == Flag::TYPE_ARGS) {
        ASSERT(args_flag == NULL);
        args_flag = f;  // Must be last in arguments.
        continue;
      }
      HeapStringAllocator string_allocator;
      StringStream buffer(&string_allocator);
243
      if (f->type() != Flag::TYPE_BOOL || *(f->bool_variable())) {
244
        buffer.Add("--%s", f->name());
245
      } else {
246
        buffer.Add("--no%s", f->name());
247
      }
248
      args->Add(buffer.ToCString().Detach());
249
      if (f->type() != Flag::TYPE_BOOL) {
250
        args->Add(ToString(f).Detach());
251 252 253
      }
    }
  }
254 255 256 257 258 259 260
  if (args_flag != NULL) {
    HeapStringAllocator string_allocator;
    StringStream buffer(&string_allocator);
    buffer.Add("--%s", args_flag->name());
    args->Add(buffer.ToCString().Detach());
    JSArguments jsargs = *args_flag->args_variable();
    for (int j = 0; j < jsargs.argc(); j++) {
261
      args->Add(StrDup(jsargs[j]));
262 263
    }
  }
264
  return args;
265 266 267
}


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 295 296 297 298 299 300 301 302 303 304 305
    if (arg[0] == 'n' && arg[1] == 'o') {
      arg += 2;  // remove "no"
      *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
306 307
      size_t n = arg - *name;
      CHECK(n < static_cast<size_t>(buffer_size));  // buffer is too small
308 309 310 311 312 313 314 315 316 317
      memcpy(buffer, *name, n);
      buffer[n] = '\0';
      *name = buffer;
      // get the value
      *value = arg + 1;
    }
  }
}


318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
inline char NormalizeChar(char ch) {
  return ch == '_' ? '-' : ch;
}


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
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
int FlagList::SetFlagsFromCommandLine(int* argc,
                                      char** argv,
                                      bool remove_flags) {
  // 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
360
      Flag* flag = FindFlag(name);
361
      if (flag == NULL) {
362 363 364 365 366 367 368
        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 {
369 370
          fprintf(stderr, "Error: unrecognized flag %s\n"
                  "Try --help for options\n", arg);
371 372
          return j;
        }
373 374 375
      }

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

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

      // handle errors
421 422
      if ((flag->type() == Flag::TYPE_BOOL && value != NULL) ||
          (flag->type() != Flag::TYPE_BOOL && is_bool) ||
423
          *endp != '\0') {
424 425
        fprintf(stderr, "Error: illegal value for flag %s of type %s\n"
                "Try --help for options\n",
426 427 428 429 430
                arg, Type2String(flag->type()));
        return j;
      }

      // remove the flag & value from the command
431 432
      if (remove_flags) {
        while (j < i) {
433
          argv[j++] = NULL;
434 435
        }
      }
436 437 438 439 440 441 442 443 444 445 446 447 448
    }
  }

  // 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;
  }

449 450
  if (FLAG_help) {
    PrintHelp();
451
    exit(0);
452
  }
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
  // parsed all flags successfully
  return 0;
}


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;
}


470
// static
471 472
int FlagList::SetFlagsFromString(const char* str, int len) {
  // make a 0-terminated copy of str
473 474
  ScopedVector<char> copy0(len + 1);
  memcpy(copy0.start(), str, len);
475 476 477
  copy0[len] = '\0';

  // strip leading white space
478
  char* copy = SkipWhiteSpace(copy0.start());
479 480 481 482 483 484 485 486 487

  // 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
488
  ScopedVector<char*> argv(argc);
489 490 491 492 493 494 495 496 497 498 499

  // 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
500
  int result = SetFlagsFromCommandLine(&argc, argv.start(), false);
501 502 503 504 505

  return result;
}


506 507 508 509 510 511 512 513 514 515
// static
void FlagList::ResetAllFlags() {
  for (size_t i = 0; i < num_flags; ++i) {
    flags[i].Reset();
  }
}


// static
void FlagList::PrintHelp() {
516 517 518 519 520 521
  printf("Usage:\n");
  printf("  shell [options] -e string\n");
  printf("    execute string in V8\n");
  printf("  shell [options] file1 file2 ... filek\n");
  printf("    run JavaScript scripts in file1, file2, ..., filek\n");
  printf("  shell [options]\n");
522 523 524
  printf("  shell [options] --shell [file1 file2 ... filek]\n");
  printf("    run an interactive JavaScript shell\n");
  printf("  d8 [options] file1 file2 ... filek\n");
525
  printf("  d8 [options]\n");
526
  printf("  d8 [options] --shell [file1 file2 ... filek]\n");
527 528
  printf("    run the new debugging shell\n\n");
  printf("Options:\n");
529 530
  for (size_t i = 0; i < num_flags; ++i) {
    Flag* f = &flags[i];
531
    SmartArrayPointer<const char> value = ToString(f);
532
    printf("  --%s (%s)\n        type: %s  default: %s\n",
533
           f->name(), f->comment(), Type2String(f->type()), *value);
534
  }
535 536
}

537 538 539 540 541 542 543 544 545 546 547 548 549 550
JSArguments::JSArguments()
    : argc_(0), argv_(NULL) {}
JSArguments::JSArguments(int argc, const char** argv)
    : argc_(argc), argv_(argv) {}
int JSArguments::argc() const { return argc_; }
const char** JSArguments::argv() { return argv_; }
const char*& JSArguments::operator[](int idx) { return argv_[idx]; }
JSArguments& JSArguments::operator=(JSArguments args) {
    argc_ = args.argc_;
    argv_ = args.argv_;
    return *this;
}


551
} }  // namespace v8::internal