1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/flags.h"
6
7#include "platform/assert.h"
8#include "vm/json_stream.h"
9#include "vm/os.h"
10
11namespace dart {
12
13DEFINE_FLAG(bool, print_flags, false, "Print flags as they are being parsed.");
14DEFINE_FLAG(bool,
15 ignore_unrecognized_flags,
16 false,
17 "Ignore unrecognized flags.");
18
19#define PRODUCT_FLAG_MACRO(name, type, default_value, comment) \
20 type FLAG_##name = \
21 Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
22
23#if defined(DEBUG)
24#define DEBUG_FLAG_MACRO(name, type, default_value, comment) \
25 type FLAG_##name = \
26 Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
27#else // defined(DEBUG)
28#define DEBUG_FLAG_MACRO(name, type, default_value, comment)
29#endif // defined(DEBUG)
30
31#if defined(PRODUCT) && defined(DART_PRECOMPILED_RUNTIME)
32// Nothing to be done for the product flag definitions.
33#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment)
34// Nothing to be done for the precompilation flag definitions.
35#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
36 default_value, comment)
37
38#elif defined(PRODUCT) // !PRECOMPILED
39// Nothing to be done for the product flag definitions.
40#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment)
41// Nothing to be done for the precompilation flag definitions.
42#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
43 default_value, comment)
44
45#elif defined(DART_PRECOMPILED_RUNTIME) // !PRODUCT
46#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
47 type FLAG_##name = \
48 Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
49// Nothing to be done for the precompilation flag definitions.
50#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
51 default_value, comment)
52
53#else // !PRODUCT && !PRECOMPILED
54#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
55 type FLAG_##name = \
56 Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
57#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
58 default_value, comment) \
59 type FLAG_##name = \
60 Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
61#endif
62
63// Define all of the non-product flags here.
64FLAG_LIST(PRODUCT_FLAG_MACRO,
65 RELEASE_FLAG_MACRO,
66 PRECOMPILE_FLAG_MACRO,
67 DEBUG_FLAG_MACRO)
68
69#undef PRODUCT_FLAG_MACRO
70#undef RELEASE_FLAG_MACRO
71#undef PRECOMPILE_FLAG_MACRO
72#undef DEBUG_FLAG_MACRO
73
74bool Flags::initialized_ = false;
75
76// List of registered flags.
77Flag** Flags::flags_ = NULL;
78intptr_t Flags::capacity_ = 0;
79intptr_t Flags::num_flags_ = 0;
80
81class Flag {
82 public:
83 enum FlagType {
84 kBoolean,
85 kInteger,
86 kUint64,
87 kString,
88 kFlagHandler,
89 kOptionHandler,
90 kNumFlagTypes
91 };
92
93 Flag(const char* name, const char* comment, void* addr, FlagType type)
94 : name_(name), comment_(comment), addr_(addr), type_(type) {}
95 Flag(const char* name, const char* comment, FlagHandler handler)
96 : name_(name),
97 comment_(comment),
98 flag_handler_(handler),
99 type_(kFlagHandler) {}
100 Flag(const char* name, const char* comment, OptionHandler handler)
101 : name_(name),
102 comment_(comment),
103 option_handler_(handler),
104 type_(kOptionHandler) {}
105
106 void Print() {
107 if (IsUnrecognized()) {
108 OS::PrintErr("%s: unrecognized\n", name_);
109 return;
110 }
111 switch (type_) {
112 case kBoolean: {
113 OS::PrintErr("%s: %s (%s)\n", name_,
114 *this->bool_ptr_ ? "true" : "false", comment_);
115 break;
116 }
117 case kInteger: {
118 OS::PrintErr("%s: %d (%s)\n", name_, *this->int_ptr_, comment_);
119 break;
120 }
121 case kUint64: {
122 OS::PrintErr("%s: %" Pu64 " (%s)\n", name_, *this->uint64_ptr_,
123 comment_);
124 break;
125 }
126 case kString: {
127 if (*this->charp_ptr_ != NULL) {
128 OS::PrintErr("%s: '%s' (%s)\n", name_, *this->charp_ptr_, comment_);
129 } else {
130 OS::PrintErr("%s: (null) (%s)\n", name_, comment_);
131 }
132 break;
133 }
134 case kOptionHandler:
135 case kFlagHandler: {
136 OS::PrintErr("%s: (%s)\n", name_, comment_);
137 break;
138 }
139 default:
140 UNREACHABLE();
141 break;
142 }
143 }
144
145 bool IsUnrecognized() const {
146 return (type_ == kBoolean) && (bool_ptr_ == NULL);
147 }
148
149 const char* name_;
150 const char* comment_;
151 union {
152 void* addr_;
153 bool* bool_ptr_;
154 int* int_ptr_;
155 uint64_t* uint64_ptr_;
156 charp* charp_ptr_;
157 FlagHandler flag_handler_;
158 OptionHandler option_handler_;
159 };
160 FlagType type_;
161 bool changed_ = false;
162};
163
164Flag* Flags::Lookup(const char* name) {
165 for (intptr_t i = 0; i < num_flags_; i++) {
166 Flag* flag = flags_[i];
167 if (strcmp(flag->name_, name) == 0) {
168 return flag;
169 }
170 }
171 return NULL;
172}
173
174bool Flags::IsSet(const char* name) {
175 Flag* flag = Lookup(name);
176 return (flag != NULL) && (flag->type_ == Flag::kBoolean) &&
177 (flag->bool_ptr_ != NULL) && (*flag->bool_ptr_ == true);
178}
179
180void Flags::Cleanup() {
181 ASSERT(initialized_);
182 initialized_ = false;
183}
184
185void Flags::AddFlag(Flag* flag) {
186 ASSERT(!initialized_);
187 if (num_flags_ == capacity_) {
188 if (flags_ == NULL) {
189 capacity_ = 256;
190 flags_ = new Flag*[capacity_];
191 } else {
192 intptr_t new_capacity = capacity_ * 2;
193 Flag** new_flags = new Flag*[new_capacity];
194 for (intptr_t i = 0; i < num_flags_; i++) {
195 new_flags[i] = flags_[i];
196 }
197 delete[] flags_;
198 flags_ = new_flags;
199 capacity_ = new_capacity;
200 }
201 }
202 flags_[num_flags_++] = flag;
203}
204
205bool Flags::Register_bool(bool* addr,
206 const char* name,
207 bool default_value,
208 const char* comment) {
209 Flag* flag = Lookup(name);
210 if (flag != NULL) {
211 ASSERT(flag->IsUnrecognized());
212 return default_value;
213 }
214 flag = new Flag(name, comment, addr, Flag::kBoolean);
215 AddFlag(flag);
216 return default_value;
217}
218
219int Flags::Register_int(int* addr,
220 const char* name,
221 int default_value,
222 const char* comment) {
223 ASSERT(Lookup(name) == NULL);
224
225 Flag* flag = new Flag(name, comment, addr, Flag::kInteger);
226 AddFlag(flag);
227
228 return default_value;
229}
230
231uint64_t Flags::Register_uint64_t(uint64_t* addr,
232 const char* name,
233 uint64_t default_value,
234 const char* comment) {
235 ASSERT(Lookup(name) == NULL);
236
237 Flag* flag = new Flag(name, comment, addr, Flag::kUint64);
238 AddFlag(flag);
239
240 return default_value;
241}
242
243const char* Flags::Register_charp(charp* addr,
244 const char* name,
245 const char* default_value,
246 const char* comment) {
247 ASSERT(Lookup(name) == NULL);
248 Flag* flag = new Flag(name, comment, addr, Flag::kString);
249 AddFlag(flag);
250 return default_value;
251}
252
253bool Flags::RegisterFlagHandler(FlagHandler handler,
254 const char* name,
255 const char* comment) {
256 ASSERT(Lookup(name) == NULL);
257 Flag* flag = new Flag(name, comment, handler);
258 AddFlag(flag);
259 return false;
260}
261
262bool Flags::RegisterOptionHandler(OptionHandler handler,
263 const char* name,
264 const char* comment) {
265 ASSERT(Lookup(name) == NULL);
266 Flag* flag = new Flag(name, comment, handler);
267 AddFlag(flag);
268 return false;
269}
270
271static void Normalize(char* s) {
272 intptr_t len = strlen(s);
273 for (intptr_t i = 0; i < len; i++) {
274 if (s[i] == '-') {
275 s[i] = '_';
276 }
277 }
278}
279
280bool Flags::SetFlagFromString(Flag* flag, const char* argument) {
281 ASSERT(!flag->IsUnrecognized());
282 switch (flag->type_) {
283 case Flag::kBoolean: {
284 if (strcmp(argument, "true") == 0) {
285 *flag->bool_ptr_ = true;
286 } else if (strcmp(argument, "false") == 0) {
287 *flag->bool_ptr_ = false;
288 } else {
289 return false;
290 }
291 break;
292 }
293 case Flag::kString: {
294 *flag->charp_ptr_ = argument == NULL ? NULL : Utils::StrDup(argument);
295 break;
296 }
297 case Flag::kInteger: {
298 char* endptr = NULL;
299 const intptr_t len = strlen(argument);
300 int base = 10;
301 if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) {
302 base = 16;
303 }
304 int val = strtol(argument, &endptr, base);
305 if (endptr == argument + len) {
306 *flag->int_ptr_ = val;
307 } else {
308 return false;
309 }
310 break;
311 }
312 case Flag::kUint64: {
313 char* endptr = NULL;
314 const intptr_t len = strlen(argument);
315 int base = 10;
316 if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) {
317 base = 16;
318 }
319 int64_t val = strtoll(argument, &endptr, base);
320 if (endptr == argument + len) {
321 *flag->uint64_ptr_ = static_cast<uint64_t>(val);
322 } else {
323 return false;
324 }
325 break;
326 }
327 case Flag::kFlagHandler: {
328 if (strcmp(argument, "true") == 0) {
329 (flag->flag_handler_)(true);
330 } else if (strcmp(argument, "false") == 0) {
331 (flag->flag_handler_)(false);
332 } else {
333 return false;
334 }
335 break;
336 }
337 case Flag::kOptionHandler: {
338 (flag->option_handler_)(argument);
339 break;
340 }
341 default: {
342 UNREACHABLE();
343 return false;
344 }
345 }
346 flag->changed_ = true;
347 return true;
348}
349
350void Flags::Parse(const char* option) {
351 // Find the beginning of the option argument, if it exists.
352 const char* equals = option;
353 while ((*equals != '\0') && (*equals != '=')) {
354 equals++;
355 }
356
357 const char* argument = NULL;
358
359 // Determine if this is an option argument.
360 if (*equals != '=') {
361 // No explicit option argument. Determine if there is a "no_" prefix
362 // preceding the name.
363 const char* const kNo1Prefix = "no_";
364 const char* const kNo2Prefix = "no-";
365 const intptr_t kNo1PrefixLen = strlen(kNo1Prefix);
366 const intptr_t kNo2PrefixLen = strlen(kNo2Prefix);
367 if (strncmp(option, kNo1Prefix, kNo1PrefixLen) == 0) {
368 option += kNo1PrefixLen; // Skip the "no_" when looking up the name.
369 argument = "false";
370 } else if (strncmp(option, kNo2Prefix, kNo2PrefixLen) == 0) {
371 option += kNo2PrefixLen; // Skip the "no-" when looking up the name.
372 argument = "false";
373 } else {
374 argument = "true";
375 }
376 } else {
377 // The argument for the option starts right after the equals sign.
378 argument = equals + 1;
379 }
380
381 // Initialize the flag name.
382 intptr_t name_len = equals - option;
383 char* name = new char[name_len + 1];
384 strncpy(name, option, name_len);
385 name[name_len] = '\0';
386 Normalize(name);
387
388 Flag* flag = Flags::Lookup(name);
389 if (flag == NULL) {
390 // Collect unrecognized flags.
391 char* new_flag = new char[name_len + 1];
392 strncpy(new_flag, option, name_len);
393 new_flag[name_len] = '\0';
394 Flags::Register_bool(NULL, new_flag, true, NULL);
395 } else {
396 // Only set values for recognized flags, skip collected
397 // unrecognized flags.
398 if (!flag->IsUnrecognized()) {
399 if (!SetFlagFromString(flag, argument)) {
400 OS::PrintErr("Ignoring flag: %s is an invalid value for flag %s\n",
401 argument, name);
402 }
403 }
404 }
405
406 delete[] name;
407}
408
409static bool IsValidFlag(const char* name,
410 const char* prefix,
411 intptr_t prefix_length) {
412 intptr_t name_length = strlen(name);
413 return ((name_length > prefix_length) &&
414 (strncmp(name, prefix, prefix_length) == 0));
415}
416
417int Flags::CompareFlagNames(const void* left, const void* right) {
418 const Flag* left_flag = *reinterpret_cast<const Flag* const*>(left);
419 const Flag* right_flag = *reinterpret_cast<const Flag* const*>(right);
420 return strcmp(left_flag->name_, right_flag->name_);
421}
422
423char* Flags::ProcessCommandLineFlags(int number_of_vm_flags,
424 const char** vm_flags) {
425 if (initialized_) {
426 return Utils::StrDup("Flags already set");
427 }
428
429 qsort(flags_, num_flags_, sizeof flags_[0], CompareFlagNames);
430
431 const char* const kPrefix = "--";
432 const intptr_t kPrefixLen = strlen(kPrefix);
433
434 int i = 0;
435 while ((i < number_of_vm_flags) &&
436 IsValidFlag(vm_flags[i], kPrefix, kPrefixLen)) {
437 const char* option = vm_flags[i] + kPrefixLen;
438 Parse(option);
439 i++;
440 }
441
442 if (!FLAG_ignore_unrecognized_flags) {
443 int unrecognized_count = 0;
444 TextBuffer error(64);
445 for (intptr_t j = 0; j < num_flags_; j++) {
446 Flag* flag = flags_[j];
447 if (flag->IsUnrecognized()) {
448 if (unrecognized_count == 0) {
449 error.Printf("Unrecognized flags: %s", flag->name_);
450 } else {
451 error.Printf(", %s", flag->name_);
452 }
453 unrecognized_count++;
454 }
455 }
456 if (unrecognized_count > 0) {
457 return error.Steal();
458 }
459 }
460 if (FLAG_print_flags) {
461 PrintFlags();
462 }
463
464 initialized_ = true;
465 return NULL;
466}
467
468bool Flags::SetFlag(const char* name, const char* value, const char** error) {
469 Flag* flag = Lookup(name);
470 if (flag == NULL) {
471 *error = "Cannot set flag: flag not found";
472 return false;
473 }
474 if (!SetFlagFromString(flag, value)) {
475 *error = "Cannot set flag: invalid value";
476 return false;
477 }
478 return true;
479}
480
481void Flags::PrintFlags() {
482 OS::PrintErr("Flag settings:\n");
483 for (intptr_t i = 0; i < num_flags_; ++i) {
484 flags_[i]->Print();
485 }
486}
487
488#ifndef PRODUCT
489void Flags::PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag) {
490 if (flag->IsUnrecognized() || flag->type_ == Flag::kFlagHandler ||
491 flag->type_ == Flag::kOptionHandler) {
492 return;
493 }
494 JSONObject jsflag(jsarr);
495 jsflag.AddProperty("name", flag->name_);
496 jsflag.AddProperty("comment", flag->comment_);
497 jsflag.AddProperty("modified", flag->changed_);
498 switch (flag->type_) {
499 case Flag::kBoolean: {
500 jsflag.AddProperty("_flagType", "Bool");
501 jsflag.AddProperty("valueAsString",
502 (*flag->bool_ptr_ ? "true" : "false"));
503 break;
504 }
505 case Flag::kInteger: {
506 jsflag.AddProperty("_flagType", "Int");
507 jsflag.AddPropertyF("valueAsString", "%d", *flag->int_ptr_);
508 break;
509 }
510 case Flag::kUint64: {
511 jsflag.AddProperty("_flagType", "UInt64");
512 jsflag.AddPropertyF("valueAsString", "%" Pu64, *flag->uint64_ptr_);
513 break;
514 }
515 case Flag::kString: {
516 jsflag.AddProperty("_flagType", "String");
517 if (flag->charp_ptr_ != NULL) {
518 jsflag.AddPropertyF("valueAsString", "%s", *flag->charp_ptr_);
519 } else {
520 // valueAsString missing means NULL.
521 }
522 break;
523 }
524 default:
525 UNREACHABLE();
526 break;
527 }
528}
529
530void Flags::PrintJSON(JSONStream* js) {
531 JSONObject jsobj(js);
532 jsobj.AddProperty("type", "FlagList");
533 JSONArray jsarr(&jsobj, "flags");
534 for (intptr_t i = 0; i < num_flags_; ++i) {
535 PrintFlagToJSONArray(&jsarr, flags_[i]);
536 }
537}
538#endif // !PRODUCT
539
540} // namespace dart
541