V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
flags.cc
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 "src/flags.h"
6 
7 #include <cctype>
8 #include <cerrno>
9 #include <cstdlib>
10 #include <sstream>
11 
12 #include "src/allocation.h"
13 #include "src/assembler.h"
14 #include "src/base/functional.h"
15 #include "src/base/platform/platform.h"
16 #include "src/ostreams.h"
17 #include "src/utils.h"
18 #include "src/wasm/wasm-limits.h"
19 
20 namespace v8 {
21 namespace internal {
22 
23 // Define all of our flags.
24 #define FLAG_MODE_DEFINE
25 #include "src/flag-definitions.h" // NOLINT(build/include)
26 
27 // Define all of our flags default values.
28 #define FLAG_MODE_DEFINE_DEFAULTS
29 #include "src/flag-definitions.h" // NOLINT(build/include)
30 
31 namespace {
32 
33 // This structure represents a single entry in the flag system, with a pointer
34 // to the actual flag, default value, comment, etc. This is designed to be POD
35 // initialized as to avoid requiring static constructors.
36 struct Flag {
37  enum FlagType {
38  TYPE_BOOL,
39  TYPE_MAYBE_BOOL,
40  TYPE_INT,
41  TYPE_UINT,
42  TYPE_UINT64,
43  TYPE_FLOAT,
44  TYPE_SIZE_T,
45  TYPE_STRING,
46  };
47 
48  FlagType type_; // What type of flag, bool, int, or string.
49  const char* name_; // Name of the flag, ex "my_flag".
50  void* valptr_; // Pointer to the global flag variable.
51  const void* defptr_; // Pointer to the default value.
52  const char* cmt_; // A comment about the flags purpose.
53  bool owns_ptr_; // Does the flag own its string value?
54 
55  FlagType type() const { return type_; }
56 
57  const char* name() const { return name_; }
58 
59  const char* comment() const { return cmt_; }
60 
61  bool* bool_variable() const {
62  DCHECK(type_ == TYPE_BOOL);
63  return reinterpret_cast<bool*>(valptr_);
64  }
65 
66  MaybeBoolFlag* maybe_bool_variable() const {
67  DCHECK(type_ == TYPE_MAYBE_BOOL);
68  return reinterpret_cast<MaybeBoolFlag*>(valptr_);
69  }
70 
71  int* int_variable() const {
72  DCHECK(type_ == TYPE_INT);
73  return reinterpret_cast<int*>(valptr_);
74  }
75 
76  unsigned int* uint_variable() const {
77  DCHECK(type_ == TYPE_UINT);
78  return reinterpret_cast<unsigned int*>(valptr_);
79  }
80 
81  uint64_t* uint64_variable() const {
82  DCHECK(type_ == TYPE_UINT64);
83  return reinterpret_cast<uint64_t*>(valptr_);
84  }
85 
86  double* float_variable() const {
87  DCHECK(type_ == TYPE_FLOAT);
88  return reinterpret_cast<double*>(valptr_);
89  }
90 
91  size_t* size_t_variable() const {
92  DCHECK(type_ == TYPE_SIZE_T);
93  return reinterpret_cast<size_t*>(valptr_);
94  }
95 
96  const char* string_value() const {
97  DCHECK(type_ == TYPE_STRING);
98  return *reinterpret_cast<const char**>(valptr_);
99  }
100 
101  void set_string_value(const char* value, bool owns_ptr) {
102  DCHECK(type_ == TYPE_STRING);
103  const char** ptr = reinterpret_cast<const char**>(valptr_);
104  if (owns_ptr_ && *ptr != nullptr) DeleteArray(*ptr);
105  *ptr = value;
106  owns_ptr_ = owns_ptr;
107  }
108 
109  bool bool_default() const {
110  DCHECK(type_ == TYPE_BOOL);
111  return *reinterpret_cast<const bool*>(defptr_);
112  }
113 
114  int int_default() const {
115  DCHECK(type_ == TYPE_INT);
116  return *reinterpret_cast<const int*>(defptr_);
117  }
118 
119  unsigned int uint_default() const {
120  DCHECK(type_ == TYPE_UINT);
121  return *reinterpret_cast<const unsigned int*>(defptr_);
122  }
123 
124  uint64_t uint64_default() const {
125  DCHECK(type_ == TYPE_UINT64);
126  return *reinterpret_cast<const uint64_t*>(defptr_);
127  }
128 
129  double float_default() const {
130  DCHECK(type_ == TYPE_FLOAT);
131  return *reinterpret_cast<const double*>(defptr_);
132  }
133 
134  size_t size_t_default() const {
135  DCHECK(type_ == TYPE_SIZE_T);
136  return *reinterpret_cast<const size_t*>(defptr_);
137  }
138 
139  const char* string_default() const {
140  DCHECK(type_ == TYPE_STRING);
141  return *reinterpret_cast<const char* const *>(defptr_);
142  }
143 
144  // Compare this flag's current value against the default.
145  bool IsDefault() const {
146  switch (type_) {
147  case TYPE_BOOL:
148  return *bool_variable() == bool_default();
149  case TYPE_MAYBE_BOOL:
150  return maybe_bool_variable()->has_value == false;
151  case TYPE_INT:
152  return *int_variable() == int_default();
153  case TYPE_UINT:
154  return *uint_variable() == uint_default();
155  case TYPE_UINT64:
156  return *uint64_variable() == uint64_default();
157  case TYPE_FLOAT:
158  return *float_variable() == float_default();
159  case TYPE_SIZE_T:
160  return *size_t_variable() == size_t_default();
161  case TYPE_STRING: {
162  const char* str1 = string_value();
163  const char* str2 = string_default();
164  if (str2 == nullptr) return str1 == nullptr;
165  if (str1 == nullptr) return str2 == nullptr;
166  return strcmp(str1, str2) == 0;
167  }
168  }
169  UNREACHABLE();
170  }
171 
172  // Set a flag back to it's default value.
173  void Reset() {
174  switch (type_) {
175  case TYPE_BOOL:
176  *bool_variable() = bool_default();
177  break;
178  case TYPE_MAYBE_BOOL:
179  *maybe_bool_variable() = MaybeBoolFlag::Create(false, false);
180  break;
181  case TYPE_INT:
182  *int_variable() = int_default();
183  break;
184  case TYPE_UINT:
185  *uint_variable() = uint_default();
186  break;
187  case TYPE_UINT64:
188  *uint64_variable() = uint64_default();
189  break;
190  case TYPE_FLOAT:
191  *float_variable() = float_default();
192  break;
193  case TYPE_SIZE_T:
194  *size_t_variable() = size_t_default();
195  break;
196  case TYPE_STRING:
197  set_string_value(string_default(), false);
198  break;
199  }
200  }
201 };
202 
203 Flag flags[] = {
204 #define FLAG_MODE_META
205 #include "src/flag-definitions.h" // NOLINT(build/include)
206 };
207 
208 const size_t num_flags = sizeof(flags) / sizeof(*flags);
209 
210 } // namespace
211 
212 
213 static const char* Type2String(Flag::FlagType type) {
214  switch (type) {
215  case Flag::TYPE_BOOL: return "bool";
216  case Flag::TYPE_MAYBE_BOOL: return "maybe_bool";
217  case Flag::TYPE_INT: return "int";
218  case Flag::TYPE_UINT:
219  return "uint";
220  case Flag::TYPE_UINT64:
221  return "uint64";
222  case Flag::TYPE_FLOAT: return "float";
223  case Flag::TYPE_SIZE_T:
224  return "size_t";
225  case Flag::TYPE_STRING: return "string";
226  }
227  UNREACHABLE();
228 }
229 
230 
231 std::ostream& operator<<(std::ostream& os, const Flag& flag) { // NOLINT
232  switch (flag.type()) {
233  case Flag::TYPE_BOOL:
234  os << (*flag.bool_variable() ? "true" : "false");
235  break;
236  case Flag::TYPE_MAYBE_BOOL:
237  os << (flag.maybe_bool_variable()->has_value
238  ? (flag.maybe_bool_variable()->value ? "true" : "false")
239  : "unset");
240  break;
241  case Flag::TYPE_INT:
242  os << *flag.int_variable();
243  break;
244  case Flag::TYPE_UINT:
245  os << *flag.uint_variable();
246  break;
247  case Flag::TYPE_UINT64:
248  os << *flag.uint64_variable();
249  break;
250  case Flag::TYPE_FLOAT:
251  os << *flag.float_variable();
252  break;
253  case Flag::TYPE_SIZE_T:
254  os << *flag.size_t_variable();
255  break;
256  case Flag::TYPE_STRING: {
257  const char* str = flag.string_value();
258  os << (str ? str : "nullptr");
259  break;
260  }
261  }
262  return os;
263 }
264 
265 
266 // static
267 std::vector<const char*>* FlagList::argv() {
268  std::vector<const char*>* args = new std::vector<const char*>(8);
269  for (size_t i = 0; i < num_flags; ++i) {
270  Flag* f = &flags[i];
271  if (!f->IsDefault()) {
272  {
273  bool disabled = f->type() == Flag::TYPE_BOOL && !*f->bool_variable();
274  std::ostringstream os;
275  os << (disabled ? "--no" : "--") << f->name();
276  args->push_back(StrDup(os.str().c_str()));
277  }
278  if (f->type() != Flag::TYPE_BOOL) {
279  std::ostringstream os;
280  os << *f;
281  args->push_back(StrDup(os.str().c_str()));
282  }
283  }
284  }
285  return args;
286 }
287 
288 
289 inline char NormalizeChar(char ch) {
290  return ch == '_' ? '-' : ch;
291 }
292 
293 // Helper function to parse flags: Takes an argument arg and splits it into
294 // a flag name and flag value (or nullptr if they are missing). negated is set
295 // if the arg started with "-no" or "--no". The buffer may be used to NUL-
296 // terminate the name, it must be large enough to hold any possible name.
297 static void SplitArgument(const char* arg, char* buffer, int buffer_size,
298  const char** name, const char** value,
299  bool* negated) {
300  *name = nullptr;
301  *value = nullptr;
302  *negated = false;
303 
304  if (arg != nullptr && *arg == '-') {
305  // find the begin of the flag name
306  arg++; // remove 1st '-'
307  if (*arg == '-') {
308  arg++; // remove 2nd '-'
309  if (arg[0] == '\0') {
310  const char* kJSArgumentsFlagName = "js_arguments";
311  *name = kJSArgumentsFlagName;
312  return;
313  }
314  }
315  if (arg[0] == 'n' && arg[1] == 'o') {
316  arg += 2; // remove "no"
317  if (NormalizeChar(arg[0]) == '-') arg++; // remove dash after "no".
318  *negated = true;
319  }
320  *name = arg;
321 
322  // find the end of the flag name
323  while (*arg != '\0' && *arg != '=')
324  arg++;
325 
326  // get the value if any
327  if (*arg == '=') {
328  // make a copy so we can NUL-terminate flag name
329  size_t n = arg - *name;
330  CHECK(n < static_cast<size_t>(buffer_size)); // buffer is too small
331  MemCopy(buffer, *name, n);
332  buffer[n] = '\0';
333  *name = buffer;
334  // get the value
335  *value = arg + 1;
336  }
337  }
338 }
339 
340 
341 static bool EqualNames(const char* a, const char* b) {
342  for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
343  if (a[i] == '\0') {
344  return true;
345  }
346  }
347  return false;
348 }
349 
350 
351 static Flag* FindFlag(const char* name) {
352  for (size_t i = 0; i < num_flags; ++i) {
353  if (EqualNames(name, flags[i].name()))
354  return &flags[i];
355  }
356  return nullptr;
357 }
358 
359 template <typename T>
360 bool TryParseUnsigned(Flag* flag, const char* arg, const char* value,
361  char** endp, T* out_val) {
362  // We do not use strtoul because it accepts negative numbers.
363  // Rejects values >= 2**63 when T is 64 bits wide but that
364  // seems like an acceptable trade-off.
365  uint64_t max = static_cast<uint64_t>(std::numeric_limits<T>::max());
366  errno = 0;
367  int64_t val = static_cast<int64_t>(strtoll(value, endp, 10));
368  if (val < 0 || static_cast<uint64_t>(val) > max || errno != 0) {
369  PrintF(stderr,
370  "Error: Value for flag %s of type %s is out of bounds "
371  "[0-%" PRIu64 "]\n",
372  arg, Type2String(flag->type()), max);
373  return false;
374  }
375  *out_val = static_cast<T>(val);
376  return true;
377 }
378 
379 // static
380 int FlagList::SetFlagsFromCommandLine(int* argc,
381  char** argv,
382  bool remove_flags) {
383  int return_code = 0;
384  // parse arguments
385  for (int i = 1; i < *argc;) {
386  int j = i; // j > 0
387  const char* arg = argv[i++];
388 
389  // split arg into flag components
390  char buffer[1*KB];
391  const char* name;
392  const char* value;
393  bool negated;
394  SplitArgument(arg, buffer, sizeof buffer, &name, &value, &negated);
395 
396  if (name != nullptr) {
397  // lookup the flag
398  Flag* flag = FindFlag(name);
399  if (flag == nullptr) {
400  if (remove_flags) {
401  // We don't recognize this flag but since we're removing
402  // the flags we recognize we assume that the remaining flags
403  // will be processed somewhere else so this flag might make
404  // sense there.
405  continue;
406  } else {
407  PrintF(stderr, "Error: unrecognized flag %s\n", arg);
408  return_code = j;
409  break;
410  }
411  }
412 
413  // if we still need a flag value, use the next argument if available
414  if (flag->type() != Flag::TYPE_BOOL &&
415  flag->type() != Flag::TYPE_MAYBE_BOOL && value == nullptr) {
416  if (i < *argc) {
417  value = argv[i++];
418  }
419  if (!value) {
420  PrintF(stderr, "Error: missing value for flag %s of type %s\n", arg,
421  Type2String(flag->type()));
422  return_code = j;
423  break;
424  }
425  }
426 
427  // set the flag
428  char* endp = const_cast<char*>(""); // *endp is only read
429  switch (flag->type()) {
430  case Flag::TYPE_BOOL:
431  *flag->bool_variable() = !negated;
432  break;
433  case Flag::TYPE_MAYBE_BOOL:
434  *flag->maybe_bool_variable() = MaybeBoolFlag::Create(true, !negated);
435  break;
436  case Flag::TYPE_INT:
437  *flag->int_variable() = static_cast<int>(strtol(value, &endp, 10));
438  break;
439  case Flag::TYPE_UINT:
440  if (!TryParseUnsigned(flag, arg, value, &endp,
441  flag->uint_variable())) {
442  return_code = j;
443  }
444  break;
445  case Flag::TYPE_UINT64:
446  if (!TryParseUnsigned(flag, arg, value, &endp,
447  flag->uint64_variable())) {
448  return_code = j;
449  }
450  break;
451  case Flag::TYPE_FLOAT:
452  *flag->float_variable() = strtod(value, &endp);
453  break;
454  case Flag::TYPE_SIZE_T:
455  if (!TryParseUnsigned(flag, arg, value, &endp,
456  flag->size_t_variable())) {
457  return_code = j;
458  }
459  break;
460  case Flag::TYPE_STRING:
461  flag->set_string_value(value ? StrDup(value) : nullptr, true);
462  break;
463  }
464 
465  // handle errors
466  bool is_bool_type = flag->type() == Flag::TYPE_BOOL ||
467  flag->type() == Flag::TYPE_MAYBE_BOOL;
468  if ((is_bool_type && value != nullptr) || (!is_bool_type && negated) ||
469  *endp != '\0') {
470  // TODO(neis): TryParseUnsigned may return with {*endp == '\0'} even in
471  // an error case.
472  PrintF(stderr, "Error: illegal value for flag %s of type %s\n", arg,
473  Type2String(flag->type()));
474  if (is_bool_type) {
475  PrintF(stderr,
476  "To set or unset a boolean flag, use --flag or --no-flag.\n");
477  }
478  return_code = j;
479  break;
480  }
481 
482  // remove the flag & value from the command
483  if (remove_flags) {
484  while (j < i) {
485  argv[j++] = nullptr;
486  }
487  }
488  }
489  }
490 
491  if (FLAG_help) {
492  PrintHelp();
493  exit(0);
494  }
495 
496  if (remove_flags) {
497  // shrink the argument list
498  int j = 1;
499  for (int i = 1; i < *argc; i++) {
500  if (argv[i] != nullptr) argv[j++] = argv[i];
501  }
502  *argc = j;
503  } else if (return_code != 0) {
504  if (return_code + 1 < *argc) {
505  PrintF(stderr, "The remaining arguments were ignored:");
506  for (int i = return_code + 1; i < *argc; ++i) {
507  PrintF(stderr, " %s", argv[i]);
508  }
509  PrintF(stderr, "\n");
510  }
511  }
512  if (return_code != 0) PrintF(stderr, "Try --help for options\n");
513 
514  return return_code;
515 }
516 
517 
518 static char* SkipWhiteSpace(char* p) {
519  while (*p != '\0' && isspace(*p) != 0) p++;
520  return p;
521 }
522 
523 
524 static char* SkipBlackSpace(char* p) {
525  while (*p != '\0' && isspace(*p) == 0) p++;
526  return p;
527 }
528 
529 
530 // static
531 int FlagList::SetFlagsFromString(const char* str, int len) {
532  // make a 0-terminated copy of str
533  ScopedVector<char> copy0(len + 1);
534  MemCopy(copy0.start(), str, len);
535  copy0[len] = '\0';
536 
537  // strip leading white space
538  char* copy = SkipWhiteSpace(copy0.start());
539 
540  // count the number of 'arguments'
541  int argc = 1; // be compatible with SetFlagsFromCommandLine()
542  for (char* p = copy; *p != '\0'; argc++) {
543  p = SkipBlackSpace(p);
544  p = SkipWhiteSpace(p);
545  }
546 
547  // allocate argument array
548  ScopedVector<char*> argv(argc);
549 
550  // split the flags string into arguments
551  argc = 1; // be compatible with SetFlagsFromCommandLine()
552  for (char* p = copy; *p != '\0'; argc++) {
553  argv[argc] = p;
554  p = SkipBlackSpace(p);
555  if (*p != '\0') *p++ = '\0'; // 0-terminate argument
556  p = SkipWhiteSpace(p);
557  }
558 
559  return SetFlagsFromCommandLine(&argc, argv.start(), false);
560 }
561 
562 
563 // static
564 void FlagList::ResetAllFlags() {
565  for (size_t i = 0; i < num_flags; ++i) {
566  flags[i].Reset();
567  }
568 }
569 
570 
571 // static
572 void FlagList::PrintHelp() {
573  CpuFeatures::Probe(false);
574  CpuFeatures::PrintTarget();
575  CpuFeatures::PrintFeatures();
576 
577  StdoutStream os;
578  os << "Synopsis:\n"
579  " shell [options] [--shell] [<file>...]\n"
580  " d8 [options] [-e <string>] [--shell] [[--module] <file>...]\n\n"
581  " -e execute a string in V8\n"
582  " --shell run an interactive JavaScript shell\n"
583  " --module execute a file as a JavaScript module\n\n"
584  "Note: the --module option is implicitly enabled for *.mjs files.\n\n"
585  "Options:\n";
586 
587  for (const Flag& f : flags) {
588  os << " --";
589  for (const char* c = f.name(); *c != '\0'; ++c) {
590  os << NormalizeChar(*c);
591  }
592  os << " (" << f.comment() << ")\n"
593  << " type: " << Type2String(f.type()) << " default: " << f
594  << "\n";
595  }
596 }
597 
598 
599 static uint32_t flag_hash = 0;
600 
601 
602 void ComputeFlagListHash() {
603  std::ostringstream modified_args_as_string;
604 #ifdef DEBUG
605  modified_args_as_string << "debug";
606 #endif // DEBUG
607  if (FLAG_embedded_builtins) {
608  modified_args_as_string << "embedded";
609  }
610  for (size_t i = 0; i < num_flags; ++i) {
611  Flag* current = &flags[i];
612  if (!current->IsDefault()) {
613  modified_args_as_string << i;
614  modified_args_as_string << *current;
615  }
616  }
617  std::string args(modified_args_as_string.str());
618  flag_hash = static_cast<uint32_t>(
619  base::hash_range(args.c_str(), args.c_str() + args.length()));
620 }
621 
622 
623 // static
624 void FlagList::EnforceFlagImplications() {
625 #define FLAG_MODE_DEFINE_IMPLICATIONS
626 #include "src/flag-definitions.h" // NOLINT(build/include)
627 #undef FLAG_MODE_DEFINE_IMPLICATIONS
628  ComputeFlagListHash();
629 }
630 
631 
632 uint32_t FlagList::Hash() { return flag_hash; }
633 } // namespace internal
634 } // namespace v8
Definition: libplatform.h:13