Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
};