PropertyName.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/FlyString.h>
  8. #include <LibJS/Runtime/StringOrSymbol.h>
  9. namespace JS {
  10. class PropertyName {
  11. public:
  12. enum class Type : u8 {
  13. Invalid,
  14. Number,
  15. String,
  16. Symbol,
  17. };
  18. enum class StringMayBeNumber {
  19. Yes,
  20. No,
  21. };
  22. static PropertyName from_value(GlobalObject& global_object, Value value)
  23. {
  24. if (value.is_empty())
  25. return {};
  26. if (value.is_symbol())
  27. return value.as_symbol();
  28. if (value.is_integral_number() && value.as_double() >= 0 && value.as_double() <= NumericLimits<u32>::max())
  29. return value.as_u32();
  30. auto string = value.to_string(global_object);
  31. if (string.is_null())
  32. return {};
  33. return string;
  34. }
  35. PropertyName() { }
  36. template<Integral T>
  37. PropertyName(T index)
  38. : m_type(Type::Number)
  39. , m_number(index)
  40. {
  41. // FIXME: Replace this with requires(IsUnsigned<T>)?
  42. // Needs changes in various places using `int` (but not actually being in the negative range)
  43. VERIFY(index >= 0);
  44. if constexpr (NumericLimits<T>::max() > NumericLimits<u32>::max())
  45. VERIFY(index <= NumericLimits<u32>::max());
  46. }
  47. PropertyName(char const* chars)
  48. : m_type(Type::String)
  49. , m_string(FlyString(chars))
  50. {
  51. }
  52. PropertyName(String const& string)
  53. : m_type(Type::String)
  54. , m_string(FlyString(string))
  55. {
  56. VERIFY(!string.is_null());
  57. }
  58. PropertyName(FlyString const& string, StringMayBeNumber string_may_be_number = StringMayBeNumber::Yes)
  59. : m_type(Type::String)
  60. , m_string_may_be_number(string_may_be_number == StringMayBeNumber::Yes)
  61. , m_string(string)
  62. {
  63. VERIFY(!string.is_null());
  64. }
  65. PropertyName(Symbol& symbol)
  66. : m_type(Type::Symbol)
  67. , m_symbol(&symbol)
  68. {
  69. }
  70. PropertyName(StringOrSymbol const& string_or_symbol)
  71. {
  72. if (string_or_symbol.is_string()) {
  73. m_string = string_or_symbol.as_string();
  74. m_type = Type::String;
  75. } else if (string_or_symbol.is_symbol()) {
  76. m_symbol = const_cast<Symbol*>(string_or_symbol.as_symbol());
  77. m_type = Type::Symbol;
  78. }
  79. }
  80. ALWAYS_INLINE Type type() const { return m_type; }
  81. bool is_valid() const { return m_type != Type::Invalid; }
  82. bool is_number() const
  83. {
  84. if (m_type == Type::Number)
  85. return true;
  86. if (m_type != Type::String || !m_string_may_be_number)
  87. return false;
  88. return const_cast<PropertyName*>(this)->try_coerce_into_number();
  89. }
  90. bool is_string() const
  91. {
  92. if (m_type != Type::String)
  93. return false;
  94. if (!m_string_may_be_number)
  95. return true;
  96. return !const_cast<PropertyName*>(this)->try_coerce_into_number();
  97. }
  98. bool is_symbol() const { return m_type == Type::Symbol; }
  99. bool try_coerce_into_number()
  100. {
  101. VERIFY(m_string_may_be_number);
  102. if (m_string.is_empty()) {
  103. m_string_may_be_number = false;
  104. return false;
  105. }
  106. if (char first = m_string.characters()[0]; first < '0' || first > '9') {
  107. m_string_may_be_number = false;
  108. return false;
  109. } else if (m_string.length() > 1 && first == '0') {
  110. m_string_may_be_number = false;
  111. return false;
  112. }
  113. auto property_index = m_string.to_uint(TrimWhitespace::No);
  114. if (!property_index.has_value()) {
  115. m_string_may_be_number = false;
  116. return false;
  117. }
  118. m_type = Type::Number;
  119. m_number = *property_index;
  120. return true;
  121. }
  122. u32 as_number() const
  123. {
  124. VERIFY(is_number());
  125. return m_number;
  126. }
  127. FlyString const& as_string() const
  128. {
  129. VERIFY(is_string());
  130. return m_string;
  131. }
  132. Symbol const* as_symbol() const
  133. {
  134. VERIFY(is_symbol());
  135. return m_symbol;
  136. }
  137. String to_string() const
  138. {
  139. VERIFY(is_valid());
  140. VERIFY(!is_symbol());
  141. if (is_string())
  142. return as_string();
  143. return String::number(as_number());
  144. }
  145. StringOrSymbol to_string_or_symbol() const
  146. {
  147. VERIFY(is_valid());
  148. VERIFY(!is_number());
  149. if (is_string())
  150. return StringOrSymbol(as_string());
  151. return StringOrSymbol(as_symbol());
  152. }
  153. private:
  154. Type m_type { Type::Invalid };
  155. bool m_string_may_be_number { true };
  156. FlyString m_string;
  157. Symbol* m_symbol { nullptr };
  158. u32 m_number { 0 };
  159. };
  160. struct PropertyNameTraits : public Traits<PropertyName> {
  161. static unsigned hash(PropertyName const& name)
  162. {
  163. VERIFY(name.is_valid());
  164. if (name.is_string())
  165. return name.as_string().hash();
  166. if (name.is_number())
  167. return int_hash(name.as_number());
  168. return ptr_hash(name.as_symbol());
  169. }
  170. static bool equals(PropertyName const& a, PropertyName const& b)
  171. {
  172. if (a.type() != b.type())
  173. return false;
  174. switch (a.type()) {
  175. case PropertyName::Type::Number:
  176. return a.as_number() == b.as_number();
  177. case PropertyName::Type::String:
  178. return a.as_string() == b.as_string();
  179. case PropertyName::Type::Symbol:
  180. return a.as_symbol() == b.as_symbol();
  181. default:
  182. VERIFY_NOT_REACHED();
  183. }
  184. }
  185. };
  186. }
  187. namespace AK {
  188. template<>
  189. struct Formatter<JS::PropertyName> : Formatter<StringView> {
  190. void format(FormatBuilder& builder, JS::PropertyName const& property_name)
  191. {
  192. if (!property_name.is_valid())
  193. Formatter<StringView>::format(builder, "<invalid PropertyName>");
  194. else if (property_name.is_number())
  195. Formatter<StringView>::format(builder, String::number(property_name.as_number()));
  196. else
  197. Formatter<StringView>::format(builder, property_name.to_string_or_symbol().to_display_string());
  198. }
  199. };
  200. }