// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/flags.h" #include "platform/assert.h" #include "vm/isolate.h" #include "vm/json_stream.h" #include "vm/os.h" namespace dart { DEFINE_FLAG(bool, print_flags, false, "Print flags as they are being parsed."); DEFINE_FLAG(bool, ignore_unrecognized_flags, false, "Ignore unrecognized flags."); #define PRODUCT_FLAG_MACRO(name, type, default_value, comment) \ type FLAG_##name = \ Flags::Register_##type(&FLAG_##name, #name, default_value, comment); #if defined(DEBUG) #define DEBUG_FLAG_MACRO(name, type, default_value, comment) \ type FLAG_##name = \ Flags::Register_##type(&FLAG_##name, #name, default_value, comment); #else // defined(DEBUG) #define DEBUG_FLAG_MACRO(name, type, default_value, comment) #endif // defined(DEBUG) #if defined(PRODUCT) && defined(DART_PRECOMPILED_RUNTIME) // Nothing to be done for the product flag definitions. #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) // Nothing to be done for the precompilation flag definitions. #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ default_value, comment) #elif defined(PRODUCT) // !PRECOMPILED // Nothing to be done for the product flag definitions. #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) // Nothing to be done for the precompilation flag definitions. #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ default_value, comment) #elif defined(DART_PRECOMPILED_RUNTIME) // !PRODUCT #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \ type FLAG_##name = \ Flags::Register_##type(&FLAG_##name, #name, default_value, comment); // Nothing to be done for the precompilation flag definitions. #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ default_value, comment) #else // !PRODUCT && !PRECOMPILED #define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \ type FLAG_##name = \ Flags::Register_##type(&FLAG_##name, #name, default_value, comment); #define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \ default_value, comment) \ type FLAG_##name = \ Flags::Register_##type(&FLAG_##name, #name, default_value, comment); #endif // Define all of the non-product flags here. FLAG_LIST(PRODUCT_FLAG_MACRO, RELEASE_FLAG_MACRO, PRECOMPILE_FLAG_MACRO, DEBUG_FLAG_MACRO) #undef PRODUCT_FLAG_MACRO #undef RELEASE_FLAG_MACRO #undef PRECOMPILE_FLAG_MACRO #undef DEBUG_FLAG_MACRO bool Flags::initialized_ = false; // List of registered flags. Flag** Flags::flags_ = NULL; intptr_t Flags::capacity_ = 0; intptr_t Flags::num_flags_ = 0; class Flag { public: enum FlagType { kBoolean, kInteger, kUint64, kString, kFlagHandler, kOptionHandler, kNumFlagTypes }; Flag(const char* name, const char* comment, void* addr, FlagType type) : name_(name), comment_(comment), addr_(addr), type_(type) {} Flag(const char* name, const char* comment, FlagHandler handler) : name_(name), comment_(comment), string_value_("false"), flag_handler_(handler), type_(kFlagHandler) {} Flag(const char* name, const char* comment, OptionHandler handler) : name_(name), comment_(comment), string_value_(nullptr), option_handler_(handler), type_(kOptionHandler) {} void Print() { if (IsUnrecognized()) { OS::PrintErr("%s: unrecognized\n", name_); return; } switch (type_) { case kBoolean: { OS::Print("%s: %s (%s)\n", name_, *this->bool_ptr_ ? "true" : "false", comment_); break; } case kInteger: { OS::Print("%s: %d (%s)\n", name_, *this->int_ptr_, comment_); break; } case kUint64: { OS::Print("%s: %" Pu64 " (%s)\n", name_, *this->uint64_ptr_, comment_); break; } case kString: { if (*this->charp_ptr_ != NULL) { OS::Print("%s: '%s' (%s)\n", name_, *this->charp_ptr_, comment_); } else { OS::Print("%s: (null) (%s)\n", name_, comment_); } break; } case kOptionHandler: case kFlagHandler: { OS::Print("%s: (%s)\n", name_, comment_); break; } default: UNREACHABLE(); break; } } bool IsUnrecognized() const { return (type_ == kBoolean) && (bool_ptr_ == NULL); } const char* name_; const char* comment_; const char* string_value_; union { void* addr_; bool* bool_ptr_; int* int_ptr_; uint64_t* uint64_ptr_; charp* charp_ptr_; FlagHandler flag_handler_; OptionHandler option_handler_; }; FlagType type_; bool changed_ = false; }; Flag* Flags::Lookup(const char* name) { for (intptr_t i = 0; i < num_flags_; i++) { Flag* flag = flags_[i]; if (strcmp(flag->name_, name) == 0) { return flag; } } return NULL; } bool Flags::IsSet(const char* name) { Flag* flag = Lookup(name); return (flag != NULL) && (flag->type_ == Flag::kBoolean) && (flag->bool_ptr_ != NULL) && (*flag->bool_ptr_ == true); } void Flags::Cleanup() { ASSERT(initialized_); initialized_ = false; } void Flags::AddFlag(Flag* flag) { ASSERT(!initialized_); if (num_flags_ == capacity_) { if (flags_ == NULL) { capacity_ = 256; flags_ = new Flag*[capacity_]; } else { intptr_t new_capacity = capacity_ * 2; Flag** new_flags = new Flag*[new_capacity]; for (intptr_t i = 0; i < num_flags_; i++) { new_flags[i] = flags_[i]; } delete[] flags_; flags_ = new_flags; capacity_ = new_capacity; } } flags_[num_flags_++] = flag; } bool Flags::Register_bool(bool* addr, const char* name, bool default_value, const char* comment) { Flag* flag = Lookup(name); if (flag != NULL) { ASSERT(flag->IsUnrecognized()); return default_value; } flag = new Flag(name, comment, addr, Flag::kBoolean); AddFlag(flag); return default_value; } int Flags::Register_int(int* addr, const char* name, int default_value, const char* comment) { ASSERT(Lookup(name) == NULL); Flag* flag = new Flag(name, comment, addr, Flag::kInteger); AddFlag(flag); return default_value; } uint64_t Flags::Register_uint64_t(uint64_t* addr, const char* name, uint64_t default_value, const char* comment) { ASSERT(Lookup(name) == NULL); Flag* flag = new Flag(name, comment, addr, Flag::kUint64); AddFlag(flag); return default_value; } const char* Flags::Register_charp(charp* addr, const char* name, const char* default_value, const char* comment) { ASSERT(Lookup(name) == NULL); Flag* flag = new Flag(name, comment, addr, Flag::kString); AddFlag(flag); return default_value; } bool Flags::RegisterFlagHandler(FlagHandler handler, const char* name, const char* comment) { ASSERT(Lookup(name) == NULL); Flag* flag = new Flag(name, comment, handler); AddFlag(flag); return false; } bool Flags::RegisterOptionHandler(OptionHandler handler, const char* name, const char* comment) { ASSERT(Lookup(name) == NULL); Flag* flag = new Flag(name, comment, handler); AddFlag(flag); return false; } static void Normalize(char* s) { intptr_t len = strlen(s); for (intptr_t i = 0; i < len; i++) { if (s[i] == '-') { s[i] = '_'; } } } bool Flags::SetFlagFromString(Flag* flag, const char* argument) { ASSERT(!flag->IsUnrecognized()); switch (flag->type_) { case Flag::kBoolean: { if (strcmp(argument, "true") == 0) { *flag->bool_ptr_ = true; } else if (strcmp(argument, "false") == 0) { *flag->bool_ptr_ = false; } else { return false; } break; } case Flag::kString: { *flag->charp_ptr_ = argument == NULL ? NULL : Utils::StrDup(argument); break; } case Flag::kInteger: { char* endptr = NULL; const intptr_t len = strlen(argument); int base = 10; if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) { base = 16; } int val = strtol(argument, &endptr, base); if (endptr == argument + len) { *flag->int_ptr_ = val; } else { return false; } break; } case Flag::kUint64: { char* endptr = NULL; const intptr_t len = strlen(argument); int base = 10; if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) { base = 16; } int64_t val = strtoll(argument, &endptr, base); if (endptr == argument + len) { *flag->uint64_ptr_ = static_cast(val); } else { return false; } break; } case Flag::kFlagHandler: { if (strcmp(argument, "true") == 0) { (flag->flag_handler_)(true); } else if (strcmp(argument, "false") == 0) { (flag->flag_handler_)(false); } else { return false; } flag->string_value_ = argument; break; } case Flag::kOptionHandler: { flag->string_value_ = argument; (flag->option_handler_)(argument); break; } default: { UNREACHABLE(); return false; } } flag->changed_ = true; return true; } void Flags::Parse(const char* option) { // Find the beginning of the option argument, if it exists. const char* equals = option; while ((*equals != '\0') && (*equals != '=')) { equals++; } const char* argument = NULL; // Determine if this is an option argument. if (*equals != '=') { // No explicit option argument. Determine if there is a "no_" prefix // preceding the name. const char* const kNo1Prefix = "no_"; const char* const kNo2Prefix = "no-"; const intptr_t kNo1PrefixLen = strlen(kNo1Prefix); const intptr_t kNo2PrefixLen = strlen(kNo2Prefix); if (strncmp(option, kNo1Prefix, kNo1PrefixLen) == 0) { option += kNo1PrefixLen; // Skip the "no_" when looking up the name. argument = "false"; } else if (strncmp(option, kNo2Prefix, kNo2PrefixLen) == 0) { option += kNo2PrefixLen; // Skip the "no-" when looking up the name. argument = "false"; } else { argument = "true"; } } else { // The argument for the option starts right after the equals sign. argument = equals + 1; } // Initialize the flag name. intptr_t name_len = equals - option; char* name = new char[name_len + 1]; strncpy(name, option, name_len); name[name_len] = '\0'; Normalize(name); Flag* flag = Flags::Lookup(name); if (flag == NULL) { // Collect unrecognized flags. char* new_flag = new char[name_len + 1]; strncpy(new_flag, option, name_len); new_flag[name_len] = '\0'; Flags::Register_bool(NULL, new_flag, true, NULL); } else { // Only set values for recognized flags, skip collected // unrecognized flags. if (!flag->IsUnrecognized()) { if (!SetFlagFromString(flag, argument)) { OS::PrintErr("Ignoring flag: %s is an invalid value for flag %s\n", argument, name); } } } delete[] name; } static bool IsValidFlag(const char* name, const char* prefix, intptr_t prefix_length) { intptr_t name_length = strlen(name); return ((name_length > prefix_length) && (strncmp(name, prefix, prefix_length) == 0)); } int Flags::CompareFlagNames(const void* left, const void* right) { const Flag* left_flag = *reinterpret_cast(left); const Flag* right_flag = *reinterpret_cast(right); return strcmp(left_flag->name_, right_flag->name_); } char* Flags::ProcessCommandLineFlags(int number_of_vm_flags, const char** vm_flags) { if (initialized_) { return Utils::StrDup("Flags already set"); } qsort(flags_, num_flags_, sizeof flags_[0], CompareFlagNames); const char* const kPrefix = "--"; const intptr_t kPrefixLen = strlen(kPrefix); int i = 0; while ((i < number_of_vm_flags) && IsValidFlag(vm_flags[i], kPrefix, kPrefixLen)) { const char* option = vm_flags[i] + kPrefixLen; Parse(option); i++; } if (!FLAG_ignore_unrecognized_flags) { int unrecognized_count = 0; TextBuffer error(64); for (intptr_t j = 0; j < num_flags_; j++) { Flag* flag = flags_[j]; if (flag->IsUnrecognized()) { if (unrecognized_count == 0) { error.Printf("Unrecognized flags: %s", flag->name_); } else { error.Printf(", %s", flag->name_); } unrecognized_count++; } } if (unrecognized_count > 0) { return error.Steal(); } } if (FLAG_print_flags) { PrintFlags(); } initialized_ = true; return NULL; } bool Flags::SetFlag(const char* name, const char* value, const char** error) { Flag* flag = Lookup(name); if (flag == NULL) { *error = "Cannot set flag: flag not found"; return false; } if (!SetFlagFromString(flag, value)) { *error = "Cannot set flag: invalid value"; return false; } return true; } void Flags::PrintFlags() { OS::Print("Flag settings:\n"); for (intptr_t i = 0; i < num_flags_; ++i) { flags_[i]->Print(); } } #ifndef PRODUCT void Flags::PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag) { if (flag->IsUnrecognized()) { return; } JSONObject jsflag(jsarr); jsflag.AddProperty("name", flag->name_); jsflag.AddProperty("comment", flag->comment_); jsflag.AddProperty("modified", flag->changed_); switch (flag->type_) { case Flag::kBoolean: { jsflag.AddProperty("_flagType", "Bool"); jsflag.AddProperty("valueAsString", (*flag->bool_ptr_ ? "true" : "false")); break; } case Flag::kInteger: { jsflag.AddProperty("_flagType", "Int"); jsflag.AddPropertyF("valueAsString", "%d", *flag->int_ptr_); break; } case Flag::kUint64: { jsflag.AddProperty("_flagType", "UInt64"); jsflag.AddPropertyF("valueAsString", "%" Pu64, *flag->uint64_ptr_); break; } case Flag::kString: { jsflag.AddProperty("_flagType", "String"); if (flag->charp_ptr_ != NULL) { jsflag.AddPropertyF("valueAsString", "%s", *flag->charp_ptr_); } else { // valueAsString missing means NULL. } break; } case Flag::kFlagHandler: { jsflag.AddProperty("_flagType", "Bool"); jsflag.AddProperty("valueAsString", flag->string_value_); break; } case Flag::kOptionHandler: { jsflag.AddProperty("_flagType", "String"); if (flag->string_value_ != nullptr) { jsflag.AddProperty("valueAsString", flag->string_value_); } else { // valueAsString missing means NULL. } break; } default: UNREACHABLE(); break; } } void Flags::PrintJSON(JSONStream* js) { JSONObject jsobj(js); jsobj.AddProperty("type", "FlagList"); JSONArray jsarr(&jsobj, "flags"); for (intptr_t i = 0; i < num_flags_; ++i) { PrintFlagToJSONArray(&jsarr, flags_[i]); } } #endif // !PRODUCT } // namespace dart