1// Copyright 2006-2008 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 <ctype.h>
6#include <stdlib.h>
7
8#include "src/v8.h"
9
10#include "src/assembler.h"
11#include "src/platform.h"
12#include "src/smart-pointers.h"
13#include "src/string-stream.h"
14
15namespace v8 {
16namespace internal {
17
18// Define all of our flags.
19#define FLAG_MODE_DEFINE
20#include "src/flag-definitions.h"
21
22// Define all of our flags default values.
23#define FLAG_MODE_DEFINE_DEFAULTS
24#include "src/flag-definitions.h"
25
26namespace {
27
28// This structure represents a single entry in the flag system, with a pointer
29// to the actual flag, default value, comment, etc.  This is designed to be POD
30// initialized as to avoid requiring static constructors.
31struct Flag {
32  enum FlagType { TYPE_BOOL, TYPE_MAYBE_BOOL, TYPE_INT, TYPE_FLOAT,
33                  TYPE_STRING, TYPE_ARGS };
34
35  FlagType type_;           // What type of flag, bool, int, or string.
36  const char* name_;        // Name of the flag, ex "my_flag".
37  void* valptr_;            // Pointer to the global flag variable.
38  const void* defptr_;      // Pointer to the default value.
39  const char* cmt_;         // A comment about the flags purpose.
40  bool owns_ptr_;           // Does the flag own its string value?
41
42  FlagType type() const { return type_; }
43
44  const char* name() const { return name_; }
45
46  const char* comment() const { return cmt_; }
47
48  bool* bool_variable() const {
49    ASSERT(type_ == TYPE_BOOL);
50    return reinterpret_cast<bool*>(valptr_);
51  }
52
53  MaybeBoolFlag* maybe_bool_variable() const {
54    ASSERT(type_ == TYPE_MAYBE_BOOL);
55    return reinterpret_cast<MaybeBoolFlag*>(valptr_);
56  }
57
58  int* int_variable() const {
59    ASSERT(type_ == TYPE_INT);
60    return reinterpret_cast<int*>(valptr_);
61  }
62
63  double* float_variable() const {
64    ASSERT(type_ == TYPE_FLOAT);
65    return reinterpret_cast<double*>(valptr_);
66  }
67
68  const char* string_value() const {
69    ASSERT(type_ == TYPE_STRING);
70    return *reinterpret_cast<const char**>(valptr_);
71  }
72
73  void set_string_value(const char* value, bool owns_ptr) {
74    ASSERT(type_ == TYPE_STRING);
75    const char** ptr = reinterpret_cast<const char**>(valptr_);
76    if (owns_ptr_ && *ptr != NULL) DeleteArray(*ptr);
77    *ptr = value;
78    owns_ptr_ = owns_ptr;
79  }
80
81  JSArguments* args_variable() const {
82    ASSERT(type_ == TYPE_ARGS);
83    return reinterpret_cast<JSArguments*>(valptr_);
84  }
85
86  bool bool_default() const {
87    ASSERT(type_ == TYPE_BOOL);
88    return *reinterpret_cast<const bool*>(defptr_);
89  }
90
91  int int_default() const {
92    ASSERT(type_ == TYPE_INT);
93    return *reinterpret_cast<const int*>(defptr_);
94  }
95
96  double float_default() const {
97    ASSERT(type_ == TYPE_FLOAT);
98    return *reinterpret_cast<const double*>(defptr_);
99  }
100
101  const char* string_default() const {
102    ASSERT(type_ == TYPE_STRING);
103    return *reinterpret_cast<const char* const *>(defptr_);
104  }
105
106  JSArguments args_default() const {
107    ASSERT(type_ == TYPE_ARGS);
108    return *reinterpret_cast<const JSArguments*>(defptr_);
109  }
110
111  // Compare this flag's current value against the default.
112  bool IsDefault() const {
113    switch (type_) {
114      case TYPE_BOOL:
115        return *bool_variable() == bool_default();
116      case TYPE_MAYBE_BOOL:
117        return maybe_bool_variable()->has_value == false;
118      case TYPE_INT:
119        return *int_variable() == int_default();
120      case TYPE_FLOAT:
121        return *float_variable() == float_default();
122      case TYPE_STRING: {
123        const char* str1 = string_value();
124        const char* str2 = string_default();
125        if (str2 == NULL) return str1 == NULL;
126        if (str1 == NULL) return str2 == NULL;
127        return strcmp(str1, str2) == 0;
128      }
129      case TYPE_ARGS:
130        return args_variable()->argc == 0;
131    }
132    UNREACHABLE();
133    return true;
134  }
135
136  // Set a flag back to it's default value.
137  void Reset() {
138    switch (type_) {
139      case TYPE_BOOL:
140        *bool_variable() = bool_default();
141        break;
142      case TYPE_MAYBE_BOOL:
143        *maybe_bool_variable() = MaybeBoolFlag::Create(false, false);
144        break;
145      case TYPE_INT:
146        *int_variable() = int_default();
147        break;
148      case TYPE_FLOAT:
149        *float_variable() = float_default();
150        break;
151      case TYPE_STRING:
152        set_string_value(string_default(), false);
153        break;
154      case TYPE_ARGS:
155        *args_variable() = args_default();
156        break;
157    }
158  }
159};
160
161Flag flags[] = {
162#define FLAG_MODE_META
163#include "src/flag-definitions.h"
164};
165
166const size_t num_flags = sizeof(flags) / sizeof(*flags);
167
168}  // namespace
169
170
171static const char* Type2String(Flag::FlagType type) {
172  switch (type) {
173    case Flag::TYPE_BOOL: return "bool";
174    case Flag::TYPE_MAYBE_BOOL: return "maybe_bool";
175    case Flag::TYPE_INT: return "int";
176    case Flag::TYPE_FLOAT: return "float";
177    case Flag::TYPE_STRING: return "string";
178    case Flag::TYPE_ARGS: return "arguments";
179  }
180  UNREACHABLE();
181  return NULL;
182}
183
184
185static SmartArrayPointer<const char> ToString(Flag* flag) {
186  HeapStringAllocator string_allocator;
187  StringStream buffer(&string_allocator);
188  switch (flag->type()) {
189    case Flag::TYPE_BOOL:
190      buffer.Add("%s", (*flag->bool_variable() ? "true" : "false"));
191      break;
192    case Flag::TYPE_MAYBE_BOOL:
193      buffer.Add("%s", flag->maybe_bool_variable()->has_value
194                       ? (flag->maybe_bool_variable()->value ? "true" : "false")
195                       : "unset");
196      break;
197    case Flag::TYPE_INT:
198      buffer.Add("%d", *flag->int_variable());
199      break;
200    case Flag::TYPE_FLOAT:
201      buffer.Add("%f", FmtElm(*flag->float_variable()));
202      break;
203    case Flag::TYPE_STRING: {
204      const char* str = flag->string_value();
205      buffer.Add("%s", str ? str : "NULL");
206      break;
207    }
208    case Flag::TYPE_ARGS: {
209      JSArguments args = *flag->args_variable();
210      if (args.argc > 0) {
211        buffer.Add("%s",  args[0]);
212        for (int i = 1; i < args.argc; i++) {
213          buffer.Add(" %s", args[i]);
214        }
215      }
216      break;
217    }
218  }
219  return buffer.ToCString();
220}
221
222
223// static
224List<const char*>* FlagList::argv() {
225  List<const char*>* args = new List<const char*>(8);
226  Flag* args_flag = NULL;
227  for (size_t i = 0; i < num_flags; ++i) {
228    Flag* f = &flags[i];
229    if (!f->IsDefault()) {
230      if (f->type() == Flag::TYPE_ARGS) {
231        ASSERT(args_flag == NULL);
232        args_flag = f;  // Must be last in arguments.
233        continue;
234      }
235      HeapStringAllocator string_allocator;
236      StringStream buffer(&string_allocator);
237      if (f->type() != Flag::TYPE_BOOL || *(f->bool_variable())) {
238        buffer.Add("--%s", f->name());
239      } else {
240        buffer.Add("--no%s", f->name());
241      }
242      args->Add(buffer.ToCString().Detach());
243      if (f->type() != Flag::TYPE_BOOL) {
244        args->Add(ToString(f).Detach());
245      }
246    }
247  }
248  if (args_flag != NULL) {
249    HeapStringAllocator string_allocator;
250    StringStream buffer(&string_allocator);
251    buffer.Add("--%s", args_flag->name());
252    args->Add(buffer.ToCString().Detach());
253    JSArguments jsargs = *args_flag->args_variable();
254    for (int j = 0; j < jsargs.argc; j++) {
255      args->Add(StrDup(jsargs[j]));
256    }
257  }
258  return args;
259}
260
261
262inline char NormalizeChar(char ch) {
263  return ch == '_' ? '-' : ch;
264}
265
266
267// Helper function to parse flags: Takes an argument arg and splits it into
268// a flag name and flag value (or NULL if they are missing). is_bool is set
269// if the arg started with "-no" or "--no". The buffer may be used to NUL-
270// terminate the name, it must be large enough to hold any possible name.
271static void SplitArgument(const char* arg,
272                          char* buffer,
273                          int buffer_size,
274                          const char** name,
275                          const char** value,
276                          bool* is_bool) {
277  *name = NULL;
278  *value = NULL;
279  *is_bool = false;
280
281  if (arg != NULL && *arg == '-') {
282    // find the begin of the flag name
283    arg++;  // remove 1st '-'
284    if (*arg == '-') {
285      arg++;  // remove 2nd '-'
286      if (arg[0] == '\0') {
287        const char* kJSArgumentsFlagName = "js_arguments";
288        *name = kJSArgumentsFlagName;
289        return;
290      }
291    }
292    if (arg[0] == 'n' && arg[1] == 'o') {
293      arg += 2;  // remove "no"
294      if (NormalizeChar(arg[0]) == '-') arg++;  // remove dash after "no".
295      *is_bool = true;
296    }
297    *name = arg;
298
299    // find the end of the flag name
300    while (*arg != '\0' && *arg != '=')
301      arg++;
302
303    // get the value if any
304    if (*arg == '=') {
305      // make a copy so we can NUL-terminate flag name
306      size_t n = arg - *name;
307      CHECK(n < static_cast<size_t>(buffer_size));  // buffer is too small
308      MemCopy(buffer, *name, n);
309      buffer[n] = '\0';
310      *name = buffer;
311      // get the value
312      *value = arg + 1;
313    }
314  }
315}
316
317
318static bool EqualNames(const char* a, const char* b) {
319  for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
320    if (a[i] == '\0') {
321      return true;
322    }
323  }
324  return false;
325}
326
327
328static Flag* FindFlag(const char* name) {
329  for (size_t i = 0; i < num_flags; ++i) {
330    if (EqualNames(name, flags[i].name()))
331      return &flags[i];
332  }
333  return NULL;
334}
335
336
337// static
338int FlagList::SetFlagsFromCommandLine(int* argc,
339                                      char** argv,
340                                      bool remove_flags) {
341  int return_code = 0;
342  // parse arguments
343  for (int i = 1; i < *argc;) {
344    int j = i;  // j > 0
345    const char* arg = argv[i++];
346
347    // split arg into flag components
348    char buffer[1*KB];
349    const char* name;
350    const char* value;
351    bool is_bool;
352    SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
353
354    if (name != NULL) {
355      // lookup the flag
356      Flag* flag = FindFlag(name);
357      if (flag == NULL) {
358        if (remove_flags) {
359          // We don't recognize this flag but since we're removing
360          // the flags we recognize we assume that the remaining flags
361          // will be processed somewhere else so this flag might make
362          // sense there.
363          continue;
364        } else {
365          PrintF(stderr, "Error: unrecognized flag %s\n"
366                 "Try --help for options\n", arg);
367          return_code = j;
368          break;
369        }
370      }
371
372      // if we still need a flag value, use the next argument if available
373      if (flag->type() != Flag::TYPE_BOOL &&
374          flag->type() != Flag::TYPE_MAYBE_BOOL &&
375          flag->type() != Flag::TYPE_ARGS &&
376          value == NULL) {
377        if (i < *argc) {
378          value = argv[i++];
379        } else {
380          PrintF(stderr, "Error: missing value for flag %s of type %s\n"
381                 "Try --help for options\n",
382                 arg, Type2String(flag->type()));
383          return_code = j;
384          break;
385        }
386      }
387
388      // set the flag
389      char* endp = const_cast<char*>("");  // *endp is only read
390      switch (flag->type()) {
391        case Flag::TYPE_BOOL:
392          *flag->bool_variable() = !is_bool;
393          break;
394        case Flag::TYPE_MAYBE_BOOL:
395          *flag->maybe_bool_variable() = MaybeBoolFlag::Create(true, !is_bool);
396          break;
397        case Flag::TYPE_INT:
398          *flag->int_variable() = strtol(value, &endp, 10);  // NOLINT
399          break;
400        case Flag::TYPE_FLOAT:
401          *flag->float_variable() = strtod(value, &endp);
402          break;
403        case Flag::TYPE_STRING:
404          flag->set_string_value(value ? StrDup(value) : NULL, true);
405          break;
406        case Flag::TYPE_ARGS: {
407          int start_pos = (value == NULL) ? i : i - 1;
408          int js_argc = *argc - start_pos;
409          const char** js_argv = NewArray<const char*>(js_argc);
410          if (value != NULL) {
411            js_argv[0] = StrDup(value);
412          }
413          for (int k = i; k < *argc; k++) {
414            js_argv[k - start_pos] = StrDup(argv[k]);
415          }
416          *flag->args_variable() = JSArguments::Create(js_argc, js_argv);
417          i = *argc;  // Consume all arguments
418          break;
419        }
420      }
421
422      // handle errors
423      bool is_bool_type = flag->type() == Flag::TYPE_BOOL ||
424          flag->type() == Flag::TYPE_MAYBE_BOOL;
425      if ((is_bool_type && value != NULL) || (!is_bool_type && is_bool) ||
426          *endp != '\0') {
427        PrintF(stderr, "Error: illegal value for flag %s of type %s\n"
428               "Try --help for options\n",
429               arg, Type2String(flag->type()));
430        return_code = j;
431        break;
432      }
433
434      // remove the flag & value from the command
435      if (remove_flags) {
436        while (j < i) {
437          argv[j++] = NULL;
438        }
439      }
440    }
441  }
442
443  // shrink the argument list
444  if (remove_flags) {
445    int j = 1;
446    for (int i = 1; i < *argc; i++) {
447      if (argv[i] != NULL)
448        argv[j++] = argv[i];
449    }
450    *argc = j;
451  }
452
453  if (FLAG_help) {
454    PrintHelp();
455    exit(0);
456  }
457  // parsed all flags successfully
458  return return_code;
459}
460
461
462static char* SkipWhiteSpace(char* p) {
463  while (*p != '\0' && isspace(*p) != 0) p++;
464  return p;
465}
466
467
468static char* SkipBlackSpace(char* p) {
469  while (*p != '\0' && isspace(*p) == 0) p++;
470  return p;
471}
472
473
474// static
475int FlagList::SetFlagsFromString(const char* str, int len) {
476  // make a 0-terminated copy of str
477  ScopedVector<char> copy0(len + 1);
478  MemCopy(copy0.start(), str, len);
479  copy0[len] = '\0';
480
481  // strip leading white space
482  char* copy = SkipWhiteSpace(copy0.start());
483
484  // count the number of 'arguments'
485  int argc = 1;  // be compatible with SetFlagsFromCommandLine()
486  for (char* p = copy; *p != '\0'; argc++) {
487    p = SkipBlackSpace(p);
488    p = SkipWhiteSpace(p);
489  }
490
491  // allocate argument array
492  ScopedVector<char*> argv(argc);
493
494  // split the flags string into arguments
495  argc = 1;  // be compatible with SetFlagsFromCommandLine()
496  for (char* p = copy; *p != '\0'; argc++) {
497    argv[argc] = p;
498    p = SkipBlackSpace(p);
499    if (*p != '\0') *p++ = '\0';  // 0-terminate argument
500    p = SkipWhiteSpace(p);
501  }
502
503  // set the flags
504  int result = SetFlagsFromCommandLine(&argc, argv.start(), false);
505
506  return result;
507}
508
509
510// static
511void FlagList::ResetAllFlags() {
512  for (size_t i = 0; i < num_flags; ++i) {
513    flags[i].Reset();
514  }
515}
516
517
518// static
519void FlagList::PrintHelp() {
520  CpuFeatures::PrintTarget();
521  CpuFeatures::PrintFeatures();
522
523  printf("Usage:\n");
524  printf("  shell [options] -e string\n");
525  printf("    execute string in V8\n");
526  printf("  shell [options] file1 file2 ... filek\n");
527  printf("    run JavaScript scripts in file1, file2, ..., filek\n");
528  printf("  shell [options]\n");
529  printf("  shell [options] --shell [file1 file2 ... filek]\n");
530  printf("    run an interactive JavaScript shell\n");
531  printf("  d8 [options] file1 file2 ... filek\n");
532  printf("  d8 [options]\n");
533  printf("  d8 [options] --shell [file1 file2 ... filek]\n");
534  printf("    run the new debugging shell\n\n");
535  printf("Options:\n");
536  for (size_t i = 0; i < num_flags; ++i) {
537    Flag* f = &flags[i];
538    SmartArrayPointer<const char> value = ToString(f);
539    printf("  --%s (%s)\n        type: %s  default: %s\n",
540           f->name(), f->comment(), Type2String(f->type()), value.get());
541  }
542}
543
544
545// static
546void FlagList::EnforceFlagImplications() {
547#define FLAG_MODE_DEFINE_IMPLICATIONS
548#include "src/flag-definitions.h"
549#undef FLAG_MODE_DEFINE_IMPLICATIONS
550}
551
552} }  // namespace v8::internal
553