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