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