Proxy's don't properly handle Symbols as PropertyKeys.
[WebKit-https.git] / Source / JavaScriptCore / runtime / Identifier.h
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
32 namespace JSC {
33
34 class ExecState;
35
36 ALWAYS_INLINE bool isIndex(uint32_t index)
37 {
38     return index != 0xFFFFFFFFU;
39 }
40
41 template <typename CharType>
42 ALWAYS_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
81 ALWAYS_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
88 class Identifier {
89     friend class Structure;
90 public:
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
164 private:
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
205 template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(LChar)
206 {
207     ASSERT(maxSingleCharacterString == 0xff);
208     return true;
209 }
210
211 template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(UChar c)
212 {
213     return (c <= maxSingleCharacterString);
214 }
215
216 template <typename T>
217 Ref<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
230 inline bool operator==(const Identifier& a, const Identifier& b)
231 {
232     return Identifier::equal(a, b);
233 }
234
235 inline bool operator!=(const Identifier& a, const Identifier& b)
236 {
237     return !Identifier::equal(a, b);
238 }
239
240 inline bool operator==(const Identifier& a, const LChar* b)
241 {
242     return Identifier::equal(a, b);
243 }
244
245 inline bool operator==(const Identifier& a, const char* b)
246 {
247     return Identifier::equal(a, reinterpret_cast<const LChar*>(b));
248 }
249
250 inline bool operator!=(const Identifier& a, const LChar* b)
251 {
252     return !Identifier::equal(a, b);
253 }
254
255 inline bool operator!=(const Identifier& a, const char* b)
256 {
257     return !Identifier::equal(a, reinterpret_cast<const LChar*>(b));
258 }
259
260 inline bool Identifier::equal(const StringImpl* r, const LChar* s)
261 {
262     return WTF::equal(r, s);
263 }
264
265 inline bool Identifier::equal(const StringImpl* r, const LChar* s, unsigned length)
266 {
267     return WTF::equal(r, s, length);
268 }
269
270 inline bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length)
271 {
272     return WTF::equal(r, s, length);
273 }
274
275 ALWAYS_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
285 JSValue 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.
288 JSValue 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
294 struct 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
299 struct IdentifierMapIndexHashTraits : HashTraits<int> {
300     static int emptyValue() { return std::numeric_limits<int>::max(); }
301     static const bool emptyValueIsZero = false;
302 };
303
304 typedef HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> IdentifierSet;
305 typedef HashMap<RefPtr<UniquedStringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, IdentifierMapIndexHashTraits> IdentifierMap;
306 typedef HashMap<UniquedStringImpl*, int, IdentifierRepHash, HashTraits<UniquedStringImpl*>, IdentifierMapIndexHashTraits> BorrowedIdentifierMap;
307
308 } // namespace JSC
309
310 namespace WTF {
311
312 template <> struct VectorTraits<JSC::Identifier> : SimpleClassVectorTraits { };
313
314 } // namespace WTF
315
316 #endif // Identifier_h