| // Copyright 2024 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_FLAGS_FLAGS_IMPL_H_ |
| #define V8_FLAGS_FLAGS_IMPL_H_ |
| |
| #include <optional> |
| #include <unordered_set> |
| |
| #include "src/base/macros.h" |
| #include "src/base/vector.h" |
| #include "src/flags/flags.h" |
| |
| namespace v8::internal { |
| |
| class V8_EXPORT_PRIVATE FlagHelpers { |
| public: |
| static constexpr char NormalizeChar(char ch) { return ch == '_' ? '-' : ch; } |
| |
| static constexpr int FlagNamesCmp(const char* a, const char* b) { |
| int i = 0; |
| char ac, bc; |
| do { |
| ac = NormalizeChar(a[i]); |
| bc = NormalizeChar(b[i]); |
| if (ac < bc) return -1; |
| if (ac > bc) return 1; |
| i++; |
| } while (ac != '\0'); |
| return 0; |
| } |
| |
| static constexpr bool EqualNames(const char* a, const char* b) { |
| return FlagNamesCmp(a, b) == 0; |
| } |
| |
| static bool EqualNameWithSuffix(const char* a, const char* b); |
| }; |
| |
| struct Flag; |
| V8_EXPORT_PRIVATE Flag* FindFlagByName(const char* name); |
| V8_EXPORT_PRIVATE Flag* FindImplicationFlagByName(const char* name); |
| |
| V8_EXPORT_PRIVATE base::Vector<Flag> Flags(); |
| |
| // Helper struct for printing normalized flag names. |
| struct FlagName { |
| const char* name; |
| bool negated; |
| |
| constexpr FlagName(const char* name, bool negated) |
| : name(name), negated(negated) { |
| DCHECK_NE('\0', name[0]); |
| DCHECK_NE('!', name[0]); |
| } |
| |
| constexpr explicit FlagName(const char* name) |
| : FlagName(name[0] == '!' ? name + 1 : name, name[0] == '!') {} |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, FlagName flag_name); |
| |
| // This structure represents a single entry in the flag system, with a pointer |
| // to the actual flag, default value, comment, etc. This is designed to be POD |
| // initialized as to avoid requiring static constructors. |
| struct Flag { |
| enum FlagType { |
| TYPE_BOOL, |
| TYPE_MAYBE_BOOL, |
| TYPE_INT, |
| TYPE_UINT, |
| TYPE_UINT64, |
| TYPE_FLOAT, |
| TYPE_SIZE_T, |
| TYPE_STRING, |
| }; |
| |
| enum class SetBy { kDefault, kWeakImplication, kImplication, kCommandLine }; |
| |
| constexpr bool IsAnyImplication(Flag::SetBy set_by) { |
| return set_by == SetBy::kWeakImplication || set_by == SetBy::kImplication; |
| } |
| |
| FlagType type_; // What type of flag, bool, int, or string. |
| void* valptr_; // Pointer to the global flag variable. |
| const void* defptr_; // Pointer to the default value. |
| bool owns_ptr_; // Does the flag own its string value? |
| SetBy set_by_ = SetBy::kDefault; |
| // Name of the flag implying this flag, if any. |
| const char* implied_by_ = nullptr; |
| #ifdef DEBUG |
| // Pointer to the flag implying this flag, if any. |
| const Flag* implied_by_ptr_ = nullptr; |
| #endif |
| |
| FlagType type() const { return type_; } |
| |
| V8_EXPORT_PRIVATE const char* name() const; |
| |
| V8_EXPORT_PRIVATE const char* comment() const; |
| |
| bool PointsTo(const void* ptr) const { return valptr_ == ptr; } |
| |
| #ifdef DEBUG |
| bool ImpliedBy(const void* ptr) const { |
| const Flag* current = this->implied_by_ptr_; |
| std::unordered_set<const Flag*> visited_flags; |
| while (current != nullptr) { |
| visited_flags.insert(current); |
| if (current->PointsTo(ptr)) return true; |
| current = current->implied_by_ptr_; |
| if (visited_flags.contains(current)) break; |
| } |
| return false; |
| } |
| #endif |
| |
| bool bool_variable() const { return GetValue<TYPE_BOOL, bool>(); } |
| |
| void set_bool_variable(bool value, SetBy set_by) { |
| SetValue<TYPE_BOOL, bool>(value, set_by); |
| } |
| |
| std::optional<bool> maybe_bool_variable() const { |
| return GetValue<TYPE_MAYBE_BOOL, std::optional<bool>>(); |
| } |
| |
| void set_maybe_bool_variable(std::optional<bool> value, SetBy set_by) { |
| SetValue<TYPE_MAYBE_BOOL, std::optional<bool>>(value, set_by); |
| } |
| |
| int int_variable() const { return GetValue<TYPE_INT, int>(); } |
| |
| void set_int_variable(int value, SetBy set_by) { |
| SetValue<TYPE_INT, int>(value, set_by); |
| } |
| |
| unsigned int uint_variable() const { |
| return GetValue<TYPE_UINT, unsigned int>(); |
| } |
| |
| void set_uint_variable(unsigned int value, SetBy set_by) { |
| SetValue<TYPE_UINT, unsigned int>(value, set_by); |
| } |
| |
| uint64_t uint64_variable() const { return GetValue<TYPE_UINT64, uint64_t>(); } |
| |
| void set_uint64_variable(uint64_t value, SetBy set_by) { |
| SetValue<TYPE_UINT64, uint64_t>(value, set_by); |
| } |
| |
| double float_variable() const { return GetValue<TYPE_FLOAT, double>(); } |
| |
| void set_float_variable(double value, SetBy set_by) { |
| SetValue<TYPE_FLOAT, double>(value, set_by); |
| } |
| |
| size_t size_t_variable() const { return GetValue<TYPE_SIZE_T, size_t>(); } |
| |
| void set_size_t_variable(size_t value, SetBy set_by) { |
| SetValue<TYPE_SIZE_T, size_t>(value, set_by); |
| } |
| |
| const char* string_value() const { |
| return GetValue<TYPE_STRING, const char*>(); |
| } |
| |
| void set_string_value(const char* new_value, bool owns_new_value, |
| SetBy set_by); |
| |
| template <typename T> |
| T GetDefaultValue() const { |
| return *reinterpret_cast<const T*>(defptr_); |
| } |
| |
| bool bool_default() const { |
| DCHECK_EQ(TYPE_BOOL, type_); |
| return GetDefaultValue<bool>(); |
| } |
| |
| int int_default() const { |
| DCHECK_EQ(TYPE_INT, type_); |
| return GetDefaultValue<int>(); |
| } |
| |
| unsigned int uint_default() const { |
| DCHECK_EQ(TYPE_UINT, type_); |
| return GetDefaultValue<unsigned int>(); |
| } |
| |
| uint64_t uint64_default() const { |
| DCHECK_EQ(TYPE_UINT64, type_); |
| return GetDefaultValue<uint64_t>(); |
| } |
| |
| double float_default() const { |
| DCHECK_EQ(TYPE_FLOAT, type_); |
| return GetDefaultValue<double>(); |
| } |
| |
| size_t size_t_default() const { |
| DCHECK_EQ(TYPE_SIZE_T, type_); |
| return GetDefaultValue<size_t>(); |
| } |
| |
| const char* string_default() const { |
| DCHECK_EQ(TYPE_STRING, type_); |
| return GetDefaultValue<const char*>(); |
| } |
| |
| static bool ShouldCheckFlagContradictions(); |
| |
| // {change_flag} indicates if we're going to change the flag value. |
| // Returns an updated value for {change_flag}, which is changed to false if a |
| // weak implication is being ignored beause a flag is already set by a normal |
| // implication or from the command-line. |
| bool CheckFlagChange(SetBy new_set_by, bool change_flag, |
| const char* implied_by = nullptr); |
| |
| bool IsReadOnly() const { |
| // See the FLAG_READONLY definition for FLAG_MODE_META. |
| return valptr_ == nullptr; |
| } |
| |
| template <FlagType flag_type, typename T> |
| T GetValue() const { |
| DCHECK_EQ(flag_type, type_); |
| if (IsReadOnly()) return GetDefaultValue<T>(); |
| return *reinterpret_cast<const FlagValue<T>*>(valptr_); |
| } |
| |
| template <FlagType flag_type, typename T> |
| void SetValue(T new_value, SetBy set_by) { |
| DCHECK_EQ(flag_type, type_); |
| bool change_flag = GetValue<flag_type, T>() != new_value; |
| change_flag = CheckFlagChange(set_by, change_flag); |
| if (change_flag) { |
| DCHECK(!IsReadOnly()); |
| *reinterpret_cast<FlagValue<T>*>(valptr_) = new_value; |
| } |
| } |
| |
| // Compare this flag's current value against the default. |
| bool IsDefault() const; |
| |
| void ReleaseDynamicAllocations(); |
| |
| // Set a flag back to its default value. |
| V8_EXPORT_PRIVATE void Reset(); |
| |
| void AllowOverwriting() { set_by_ = SetBy::kDefault; } |
| }; |
| |
| } // namespace v8::internal |
| |
| #endif // V8_FLAGS_FLAGS_IMPL_H_ |