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