/src/serenity/Userland/Libraries/LibJS/Runtime/StringOrSymbol.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/DeprecatedFlyString.h> |
10 | | #include <LibJS/Runtime/PrimitiveString.h> |
11 | | #include <LibJS/Runtime/Symbol.h> |
12 | | #include <LibJS/Runtime/Value.h> |
13 | | |
14 | | namespace JS { |
15 | | |
16 | | class StringOrSymbol { |
17 | | public: |
18 | | StringOrSymbol() |
19 | 1.22k | : m_bits(0) |
20 | 1.22k | { |
21 | 1.22k | } |
22 | | |
23 | | StringOrSymbol(char const* chars) |
24 | | : StringOrSymbol(DeprecatedFlyString(chars)) |
25 | 0 | { |
26 | 0 | } |
27 | | |
28 | | StringOrSymbol(ByteString const& string) |
29 | | : StringOrSymbol(DeprecatedFlyString(string)) |
30 | 0 | { |
31 | 0 | } |
32 | | |
33 | | StringOrSymbol(DeprecatedFlyString const& string) |
34 | 84.5k | : m_string(string) |
35 | 84.5k | { |
36 | 84.5k | } |
37 | | |
38 | | ~StringOrSymbol() |
39 | 480k | { |
40 | 480k | if (is_string()) |
41 | 422k | m_string.~DeprecatedFlyString(); |
42 | 480k | } |
43 | | |
44 | | StringOrSymbol(Symbol const* symbol) |
45 | 1.30k | : m_symbol_with_tag(symbol) |
46 | 1.30k | { |
47 | 1.30k | set_symbol_flag(); |
48 | 1.30k | } |
49 | | |
50 | | StringOrSymbol(StringOrSymbol const& other) |
51 | 343k | { |
52 | 343k | if (other.is_string()) |
53 | 337k | new (&m_string) DeprecatedFlyString(other.m_string); |
54 | 5.10k | else |
55 | 5.10k | m_bits = other.m_bits; |
56 | 343k | } |
57 | | |
58 | | StringOrSymbol(StringOrSymbol&& other) |
59 | 50.4k | { |
60 | 50.4k | if (other.is_string()) |
61 | 48.6k | new (&m_string) DeprecatedFlyString(move(other.m_string)); |
62 | 1.76k | else |
63 | 1.76k | m_bits = exchange(other.m_bits, 0); |
64 | 50.4k | } |
65 | | |
66 | 1.64M | ALWAYS_INLINE bool is_valid() const { return m_bits != 0; } |
67 | 7.23k | ALWAYS_INLINE bool is_symbol() const { return is_valid() && (m_bits & 2); } |
68 | 1.62M | ALWAYS_INLINE bool is_string() const { return is_valid() && !(m_bits & 2); } |
69 | | |
70 | | ALWAYS_INLINE DeprecatedFlyString as_string() const |
71 | 0 | { |
72 | 0 | VERIFY(is_string()); |
73 | 0 | return m_string; |
74 | 0 | } |
75 | | |
76 | | ALWAYS_INLINE Symbol const* as_symbol() const |
77 | 5.36k | { |
78 | 5.36k | VERIFY(is_symbol()); |
79 | 5.36k | return reinterpret_cast<Symbol const*>(m_bits & ~2ULL); |
80 | 5.36k | } |
81 | | |
82 | | ByteString to_display_string() const |
83 | 0 | { |
84 | 0 | if (is_string()) |
85 | 0 | return as_string(); |
86 | 0 | if (is_symbol()) |
87 | 0 | return as_symbol()->descriptive_string().release_value_but_fixme_should_propagate_errors().to_byte_string(); |
88 | 0 | VERIFY_NOT_REACHED(); |
89 | 0 | } |
90 | | |
91 | | Value to_value(VM& vm) const |
92 | 0 | { |
93 | 0 | if (is_string()) |
94 | 0 | return PrimitiveString::create(vm, as_string()); |
95 | 0 | if (is_symbol()) |
96 | 0 | return const_cast<Symbol*>(as_symbol()); |
97 | 0 | return {}; |
98 | 0 | } |
99 | | |
100 | | void visit_edges(Cell::Visitor& visitor) |
101 | 0 | { |
102 | 0 | if (is_symbol()) |
103 | 0 | visitor.visit(const_cast<Symbol*>(as_symbol())); |
104 | 0 | } |
105 | | |
106 | | ALWAYS_INLINE bool operator==(StringOrSymbol const& other) const |
107 | 184k | { |
108 | 184k | if (is_string()) |
109 | 183k | return other.is_string() && m_string == other.m_string; |
110 | 936 | if (is_symbol()) |
111 | 936 | return other.is_symbol() && as_symbol() == other.as_symbol(); |
112 | 0 | return true; |
113 | 936 | } |
114 | | |
115 | | StringOrSymbol& operator=(StringOrSymbol const& other) |
116 | 0 | { |
117 | 0 | if (this != &other) { |
118 | 0 | this->~StringOrSymbol(); |
119 | 0 | new (this) StringOrSymbol(other); |
120 | 0 | } |
121 | 0 | return *this; |
122 | 0 | } |
123 | | |
124 | | StringOrSymbol& operator=(StringOrSymbol&& other) |
125 | 0 | { |
126 | 0 | if (this != &other) { |
127 | 0 | this->~StringOrSymbol(); |
128 | 0 | new (this) StringOrSymbol(move(other)); |
129 | 0 | } |
130 | 0 | return *this; |
131 | 0 | } |
132 | | |
133 | | unsigned hash() const |
134 | 380k | { |
135 | 380k | if (is_string()) |
136 | 375k | return m_string.hash(); |
137 | 4.73k | return ptr_hash(as_symbol()); |
138 | 380k | } |
139 | | |
140 | | private: |
141 | | ALWAYS_INLINE void set_symbol_flag() |
142 | 1.30k | { |
143 | 1.30k | m_bits |= 2; |
144 | 1.30k | } |
145 | | |
146 | | union { |
147 | | DeprecatedFlyString m_string; |
148 | | Symbol const* m_symbol_with_tag; |
149 | | uintptr_t m_bits; |
150 | | }; |
151 | | }; |
152 | | |
153 | | static_assert(sizeof(StringOrSymbol) == sizeof(uintptr_t)); |
154 | | |
155 | | } |
156 | | |
157 | | template<> |
158 | | struct AK::Traits<JS::StringOrSymbol> : public DefaultTraits<JS::StringOrSymbol> { |
159 | | static unsigned hash(JS::StringOrSymbol const& key) |
160 | 380k | { |
161 | 380k | return key.hash(); |
162 | 380k | } |
163 | | }; |