1/*
2 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#ifndef Identifier_h
22#define Identifier_h
23
24#include "VM.h"
25#include <wtf/Optional.h>
26#include <wtf/ThreadSpecific.h>
27#include <wtf/WTFThreadData.h>
28#include <wtf/text/CString.h>
29#include <wtf/text/UniquedStringImpl.h>
30#include <wtf/text/WTFString.h>
31
32namespace JSC {
33
34class ExecState;
35
36ALWAYS_INLINE bool isIndex(uint32_t index)
37{
38 return index != 0xFFFFFFFFU;
39}
40
41template <typename CharType>
42ALWAYS_INLINE Optional<uint32_t> parseIndex(const CharType* characters, unsigned length)
43{
44 // An empty string is not a number.
45 if (!length)
46 return Nullopt;
47
48 // Get the first character, turning it into a digit.
49 uint32_t value = characters[0] - '0';
50 if (value > 9)
51 return Nullopt;
52
53 // Check for leading zeros. If the first characher is 0, then the
54 // length of the string must be one - e.g. "042" is not equal to "42".
55 if (!value && length > 1)
56 return Nullopt;
57
58 while (--length) {
59 // Multiply value by 10, checking for overflow out of 32 bits.
60 if (value > 0xFFFFFFFFU / 10)
61 return Nullopt;
62 value *= 10;
63
64 // Get the next character, turning it into a digit.
65 uint32_t newValue = *(++characters) - '0';
66 if (newValue > 9)
67 return Nullopt;
68
69 // Add in the old value, checking for overflow out of 32 bits.
70 newValue += value;
71 if (newValue < value)
72 return Nullopt;
73 value = newValue;
74 }
75
76 if (!isIndex(value))
77 return Nullopt;
78 return value;
79}
80
81ALWAYS_INLINE Optional<uint32_t> parseIndex(StringImpl& impl)
82{
83 if (impl.is8Bit())
84 return parseIndex(impl.characters8(), impl.length());
85 return parseIndex(impl.characters16(), impl.length());
86}
87
88class Identifier {
89 friend class Structure;
90public:
91 Identifier() { }
92 enum EmptyIdentifierFlag { EmptyIdentifier };
93 Identifier(EmptyIdentifierFlag) : m_string(StringImpl::empty()) { ASSERT(m_string.impl()->isAtomic()); }
94
95 const String& string() const { return m_string; }
96 UniquedStringImpl* impl() const { return static_cast<UniquedStringImpl*>(m_string.impl()); }
97
98 int length() const { return m_string.length(); }
99
100 CString ascii() const { return m_string.ascii(); }
101 CString utf8() const { return m_string.utf8(); }
102
103 // There's 2 functions to construct Identifier from string, (1) fromString and (2) fromUid.
104 // They have different meanings in keeping or discarding symbol-ness of strings.
105 // (1): fromString
106 // Just construct Identifier from string. String held by Identifier is always atomized.
107 // Symbol-ness of StringImpl*, which represents that the string is inteded to be used for ES6 Symbols, is discarded.
108 // So a constructed Identifier never represents a symbol.
109 // (2): fromUid
110 // `StringImpl* uid` represents ether String or Symbol property.
111 // fromUid keeps symbol-ness of provided StringImpl* while fromString discards it.
112 // Use fromUid when constructing Identifier from StringImpl* which may represent symbols.
113
114 // Only to be used with string literals.
115 template<unsigned charactersCount>
116 static Identifier fromString(VM*, const char (&characters)[charactersCount]);
117 template<unsigned charactersCount>
118 static Identifier fromString(ExecState*, const char (&characters)[charactersCount]);
119 static Identifier fromString(VM*, const LChar*, int length);
120 static Identifier fromString(VM*, const UChar*, int length);
121 static Identifier fromString(VM*, const String&);
122 static Identifier fromString(ExecState*, AtomicStringImpl*);
123 static Identifier fromString(ExecState*, const AtomicString&);
124 static Identifier fromString(ExecState*, const String&);
125 static Identifier fromString(ExecState*, const char*);
126
127 static Identifier fromUid(VM*, UniquedStringImpl* uid);
128 static Identifier fromUid(ExecState*, UniquedStringImpl* uid);
129 static Identifier fromUid(const PrivateName&);
130
131 static Identifier createLCharFromUChar(VM* vm, const UChar* s, int length) { return Identifier(vm, add8(vm, s, length)); }
132
133 JS_EXPORT_PRIVATE static Identifier from(ExecState*, unsigned y);
134 JS_EXPORT_PRIVATE static Identifier from(ExecState*, int y);
135 static Identifier from(ExecState*, double y);
136 static Identifier from(VM*, unsigned y);
137 static Identifier from(VM*, int y);
138 static Identifier from(VM*, double y);
139
140 bool isNull() const { return m_string.isNull(); }
141 bool isEmpty() const { return m_string.isEmpty(); }
142 bool isSymbol() const { return !isNull() && impl()->isSymbol(); }
143
144 friend bool operator==(const Identifier&, const Identifier&);
145 friend bool operator!=(const Identifier&, const Identifier&);
146
147 friend bool operator==(const Identifier&, const LChar*);
148 friend bool operator==(const Identifier&, const char*);
149 friend bool operator!=(const Identifier&, const LChar*);
150 friend bool operator!=(const Identifier&, const char*);
151
152 static bool equal(const StringImpl*, const LChar*);
153 static inline bool equal(const StringImpl*a, const char*b) { return Identifier::equal(a, reinterpret_cast<const LChar*>(b)); };
154 static bool equal(const StringImpl*, const LChar*, unsigned length);
155 static bool equal(const StringImpl*, const UChar*, unsigned length);
156 static bool equal(const StringImpl* a, const StringImpl* b) { return ::equal(a, b); }
157
158 // Only to be used with string literals.
159 JS_EXPORT_PRIVATE static Ref<StringImpl> add(VM*, const char*);
160 JS_EXPORT_PRIVATE static Ref<StringImpl> add(ExecState*, const char*);
161
162 void dump(PrintStream&) const;
163
164private:
165 String m_string;
166
167 // Only to be used with string literals.
168 template<unsigned charactersCount>
169 Identifier(VM* vm, const char (&characters)[charactersCount]) : m_string(add(vm, characters)) { ASSERT(m_string.impl()->isAtomic()); }
170
171 Identifier(VM* vm, const LChar* s, int length) : m_string(add(vm, s, length)) { ASSERT(m_string.impl()->isAtomic()); }
172 Identifier(VM* vm, const UChar* s, int length) : m_string(add(vm, s, length)) { ASSERT(m_string.impl()->isAtomic()); }
173 Identifier(ExecState*, AtomicStringImpl*);
174 Identifier(ExecState*, const AtomicString&);
175 Identifier(VM* vm, const String& string) : m_string(add(vm, string.impl())) { ASSERT(m_string.impl()->isAtomic()); }
176 Identifier(VM* vm, StringImpl* rep) : m_string(add(vm, rep)) { ASSERT(m_string.impl()->isAtomic()); }
177
178 Identifier(SymbolImpl& uid)
179 : m_string(&uid)
180 {
181 }
182
183 template <typename CharType>
184 ALWAYS_INLINE static uint32_t toUInt32FromCharacters(const CharType* characters, unsigned length, bool& ok);
185
186 static bool equal(const Identifier& a, const Identifier& b) { return a.m_string.impl() == b.m_string.impl(); }
187 static bool equal(const Identifier& a, const LChar* b) { return equal(a.m_string.impl(), b); }
188
189 template <typename T> static Ref<StringImpl> add(VM*, const T*, int length);
190 static Ref<StringImpl> add8(VM*, const UChar*, int length);
191 template <typename T> ALWAYS_INLINE static bool canUseSingleCharacterString(T);
192
193 static Ref<StringImpl> add(ExecState*, StringImpl*);
194 static Ref<StringImpl> add(VM*, StringImpl*);
195
196#ifndef NDEBUG
197 JS_EXPORT_PRIVATE static void checkCurrentAtomicStringTable(ExecState*);
198 JS_EXPORT_PRIVATE static void checkCurrentAtomicStringTable(VM*);
199#else
200 JS_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH static void checkCurrentAtomicStringTable(ExecState*);
201 JS_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH static void checkCurrentAtomicStringTable(VM*);
202#endif
203};
204
205template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(LChar)
206{
207 ASSERT(maxSingleCharacterString == 0xff);
208 return true;
209}
210
211template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(UChar c)
212{
213 return (c <= maxSingleCharacterString);
214}
215
216template <typename T>
217Ref<StringImpl> Identifier::add(VM* vm, const T* s, int length)
218{
219 if (length == 1) {
220 T c = s[0];
221 if (canUseSingleCharacterString(c))
222 return *vm->smallStrings.singleCharacterStringRep(c);
223 }
224 if (!length)
225 return *StringImpl::empty();
226
227 return *AtomicStringImpl::add(s, length);
228}
229
230inline bool operator==(const Identifier& a, const Identifier& b)
231{
232 return Identifier::equal(a, b);
233}
234
235inline bool operator!=(const Identifier& a, const Identifier& b)
236{
237 return !Identifier::equal(a, b);
238}
239
240inline bool operator==(const Identifier& a, const LChar* b)
241{
242 return Identifier::equal(a, b);
243}
244
245inline bool operator==(const Identifier& a, const char* b)
246{
247 return Identifier::equal(a, reinterpret_cast<const LChar*>(b));
248}
249
250inline bool operator!=(const Identifier& a, const LChar* b)
251{
252 return !Identifier::equal(a, b);
253}
254
255inline bool operator!=(const Identifier& a, const char* b)
256{
257 return !Identifier::equal(a, reinterpret_cast<const LChar*>(b));
258}
259
260inline bool Identifier::equal(const StringImpl* r, const LChar* s)
261{
262 return WTF::equal(r, s);
263}
264
265inline bool Identifier::equal(const StringImpl* r, const LChar* s, unsigned length)
266{
267 return WTF::equal(r, s, length);
268}
269
270inline bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length)
271{
272 return WTF::equal(r, s, length);
273}
274
275ALWAYS_INLINE Optional<uint32_t> parseIndex(const Identifier& identifier)
276{
277 auto uid = identifier.impl();
278 if (!uid)
279 return Nullopt;
280 if (uid->isSymbol())
281 return Nullopt;
282 return parseIndex(*uid);
283}
284
285JSValue identifierToJSValue(VM&, const Identifier&);
286// This will stringify private symbols. When leaking JSValues to
287// non-internal code, make sure to use this function and not the above one.
288JSValue identifierToSafePublicJSValue(VM&, const Identifier&);
289
290// FIXME: It may be better for this to just be a typedef for PtrHash, since PtrHash may be cheaper to
291// compute than loading the StringImpl's hash from memory. That change would also reduce the likelihood of
292// crashes in code that somehow dangled a StringImpl.
293// https://bugs.webkit.org/show_bug.cgi?id=150137
294struct IdentifierRepHash : PtrHash<RefPtr<UniquedStringImpl>> {
295 static unsigned hash(const RefPtr<UniquedStringImpl>& key) { return key->existingSymbolAwareHash(); }
296 static unsigned hash(UniquedStringImpl* key) { return key->existingSymbolAwareHash(); }
297};
298
299struct IdentifierMapIndexHashTraits : HashTraits<int> {
300 static int emptyValue() { return std::numeric_limits<int>::max(); }
301 static const bool emptyValueIsZero = false;
302};
303
304typedef HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> IdentifierSet;
305typedef HashMap<RefPtr<UniquedStringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, IdentifierMapIndexHashTraits> IdentifierMap;
306typedef HashMap<UniquedStringImpl*, int, IdentifierRepHash, HashTraits<UniquedStringImpl*>, IdentifierMapIndexHashTraits> BorrowedIdentifierMap;
307
308} // namespace JSC
309
310namespace WTF {
311
312template <> struct VectorTraits<JSC::Identifier> : SimpleClassVectorTraits { };
313
314} // namespace WTF
315
316#endif // Identifier_h
317