1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) |
3 | * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
4 | * Copyright (C) 2009 Google Inc. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #ifndef UString_h |
24 | #define UString_h |
25 | |
26 | #include "Collector.h" |
27 | #include "UStringImpl.h" |
28 | #include <stdint.h> |
29 | #include <string.h> |
30 | #include <wtf/Assertions.h> |
31 | #include <wtf/CrossThreadRefCounted.h> |
32 | #include <wtf/OwnFastMallocPtr.h> |
33 | #include <wtf/PassRefPtr.h> |
34 | #include <wtf/PtrAndFlags.h> |
35 | #include <wtf/RefPtr.h> |
36 | #include <wtf/Vector.h> |
37 | #include <wtf/unicode/Unicode.h> |
38 | |
39 | #if PLATFORM(QT) |
40 | #include <QtCore/qstring.h> |
41 | #endif |
42 | |
43 | namespace JSC { |
44 | |
45 | using WTF::PlacementNewAdoptType; |
46 | using WTF::PlacementNewAdopt; |
47 | |
48 | class CString { |
49 | public: |
50 | CString() |
51 | : m_length(0) |
52 | , m_data(0) |
53 | { |
54 | } |
55 | |
56 | CString(const char*); |
57 | CString(const char*, size_t); |
58 | CString(const CString&); |
59 | |
60 | ~CString(); |
61 | |
62 | static CString adopt(char*, size_t); // buffer should be allocated with new[]. |
63 | |
64 | CString& append(const CString&); |
65 | CString& operator=(const char* c); |
66 | CString& operator=(const CString&); |
67 | CString& operator+=(const CString& c) { return append(c); } |
68 | |
69 | size_t size() const { return m_length; } |
70 | const char* c_str() const { return m_data; } |
71 | |
72 | private: |
73 | size_t m_length; |
74 | char* m_data; |
75 | }; |
76 | |
77 | bool operator==(const CString&, const CString&); |
78 | |
79 | typedef Vector<char, 32> CStringBuffer; |
80 | |
81 | class UString { |
82 | friend class JIT; |
83 | |
84 | public: |
85 | #if PLATFORM(QT) |
86 | operator QT_PREPEND_NAMESPACE(QString)() const |
87 | { |
88 | return QT_PREPEND_NAMESPACE(QString)(reinterpret_cast<const QT_PREPEND_NAMESPACE(QChar)*>(this->data()), this->size()); |
89 | } |
90 | |
91 | UString(const QT_PREPEND_NAMESPACE(QString)& str) |
92 | { |
93 | *this = JSC::UString(reinterpret_cast<const UChar*>(str.constData()), str.length()); |
94 | } |
95 | #endif |
96 | typedef UStringImpl Rep; |
97 | |
98 | public: |
99 | // UString constructors passed char*s assume ISO Latin-1 encoding; for UTF8 use 'createFromUTF8', below. |
100 | UString(); |
101 | UString(const char*); // Constructor for null-terminated string. |
102 | UString(const char*, int length); |
103 | UString(const UChar*, int length); |
104 | UString(const Vector<UChar>& buffer); |
105 | |
106 | UString(const UString& s) |
107 | : m_rep(s.m_rep) |
108 | { |
109 | } |
110 | |
111 | // Special constructor for cases where we overwrite an object in place. |
112 | UString(PlacementNewAdoptType) |
113 | : m_rep(PlacementNewAdopt) |
114 | { |
115 | } |
116 | |
117 | ~UString() |
118 | { |
119 | } |
120 | |
121 | template<size_t inlineCapacity> |
122 | static PassRefPtr<UStringImpl> adopt(Vector<UChar, inlineCapacity>& vector) |
123 | { |
124 | return Rep::adopt(vector); |
125 | } |
126 | |
127 | static UString createFromUTF8(const char*); |
128 | |
129 | static UString from(int); |
130 | static UString from(long long); |
131 | static UString from(unsigned int); |
132 | static UString from(long); |
133 | static UString from(double); |
134 | |
135 | struct Range { |
136 | public: |
137 | Range(int pos, int len) |
138 | : position(pos) |
139 | , length(len) |
140 | { |
141 | } |
142 | |
143 | Range() |
144 | { |
145 | } |
146 | |
147 | int position; |
148 | int length; |
149 | }; |
150 | |
151 | UString spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const; |
152 | |
153 | UString replaceRange(int rangeStart, int RangeEnd, const UString& replacement) const; |
154 | |
155 | bool getCString(CStringBuffer&) const; |
156 | |
157 | // NOTE: This method should only be used for *debugging* purposes as it |
158 | // is neither Unicode safe nor free from side effects nor thread-safe. |
159 | char* ascii() const; |
160 | |
161 | /** |
162 | * Convert the string to UTF-8, assuming it is UTF-16 encoded. |
163 | * In non-strict mode, this function is tolerant of badly formed UTF-16, it |
164 | * can create UTF-8 strings that are invalid because they have characters in |
165 | * the range U+D800-U+DDFF, U+FFFE, or U+FFFF, but the UTF-8 string is |
166 | * guaranteed to be otherwise valid. |
167 | * In strict mode, error is returned as null CString. |
168 | */ |
169 | CString UTF8String(bool strict = false) const; |
170 | |
171 | UString& operator=(const char*c); |
172 | |
173 | const UChar* data() const { return m_rep->data(); } |
174 | |
175 | bool isNull() const { return m_rep == &Rep::null(); } |
176 | bool isEmpty() const { return !m_rep->size(); } |
177 | |
178 | bool is8Bit() const; |
179 | |
180 | int size() const { return m_rep->size(); } |
181 | |
182 | UChar operator[](int pos) const; |
183 | |
184 | double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const; |
185 | double toDouble(bool tolerateTrailingJunk) const; |
186 | double toDouble() const; |
187 | |
188 | uint32_t toUInt32(bool* ok = 0) const; |
189 | uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const; |
190 | uint32_t toStrictUInt32(bool* ok = 0) const; |
191 | |
192 | unsigned toArrayIndex(bool* ok = 0) const; |
193 | |
194 | int find(const UString& f, int pos = 0) const; |
195 | int find(UChar, int pos = 0) const; |
196 | int rfind(const UString& f, int pos) const; |
197 | int rfind(UChar, int pos) const; |
198 | |
199 | UString substr(int pos = 0, int len = -1) const; |
200 | |
201 | static const UString& null() { return *nullUString; } |
202 | |
203 | Rep* rep() const { return m_rep.get(); } |
204 | static Rep* nullRep(); |
205 | |
206 | UString(PassRefPtr<Rep> r) |
207 | : m_rep(r) |
208 | { |
209 | ASSERT(m_rep); |
210 | } |
211 | |
212 | size_t cost() const { return m_rep->cost(); } |
213 | |
214 | private: |
215 | void makeNull(); |
216 | |
217 | RefPtr<Rep> m_rep; |
218 | static UString* nullUString; |
219 | |
220 | friend void initializeUString(); |
221 | friend bool operator==(const UString&, const UString&); |
222 | }; |
223 | |
224 | ALWAYS_INLINE bool operator==(const UString& s1, const UString& s2) |
225 | { |
226 | int size = s1.size(); |
227 | switch (size) { |
228 | case 0: |
229 | return !s2.size(); |
230 | case 1: |
231 | return s2.size() == 1 && s1.data()[0] == s2.data()[0]; |
232 | case 2: { |
233 | if (s2.size() != 2) |
234 | return false; |
235 | const UChar* d1 = s1.data(); |
236 | const UChar* d2 = s2.data(); |
237 | return (d1[0] == d2[0]) & (d1[1] == d2[1]); |
238 | } |
239 | default: |
240 | return s2.size() == size && memcmp(s1: s1.data(), s2: s2.data(), n: size * sizeof(UChar)) == 0; |
241 | } |
242 | } |
243 | |
244 | |
245 | inline bool operator!=(const UString& s1, const UString& s2) |
246 | { |
247 | return !JSC::operator==(s1, s2); |
248 | } |
249 | |
250 | bool operator<(const UString& s1, const UString& s2); |
251 | bool operator>(const UString& s1, const UString& s2); |
252 | |
253 | bool operator==(const UString& s1, const char* s2); |
254 | |
255 | inline bool operator!=(const UString& s1, const char* s2) |
256 | { |
257 | return !JSC::operator==(s1, s2); |
258 | } |
259 | |
260 | inline bool operator==(const char *s1, const UString& s2) |
261 | { |
262 | return operator==(s1: s2, s2: s1); |
263 | } |
264 | |
265 | inline bool operator!=(const char *s1, const UString& s2) |
266 | { |
267 | return !JSC::operator==(s1, s2); |
268 | } |
269 | |
270 | int compare(const UString&, const UString&); |
271 | |
272 | inline UString::UString() |
273 | : m_rep(&Rep::null()) |
274 | { |
275 | } |
276 | |
277 | // Rule from ECMA 15.2 about what an array index is. |
278 | // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1. |
279 | inline unsigned UString::toArrayIndex(bool* ok) const |
280 | { |
281 | unsigned i = toStrictUInt32(ok); |
282 | if (ok && i >= 0xFFFFFFFFU) |
283 | *ok = false; |
284 | return i; |
285 | } |
286 | |
287 | // We'd rather not do shared substring append for small strings, since |
288 | // this runs too much risk of a tiny initial string holding down a |
289 | // huge buffer. |
290 | // FIXME: this should be size_t but that would cause warnings until we |
291 | // fix UString sizes to be size_t instead of int |
292 | static const int minShareSize = Heap::minExtraCost / sizeof(UChar); |
293 | |
294 | struct IdentifierRepHash : PtrHash<RefPtr<JSC::UString::Rep> > { |
295 | static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->existingHash(); } |
296 | static unsigned hash(JSC::UString::Rep* key) { return key->existingHash(); } |
297 | }; |
298 | |
299 | void initializeUString(); |
300 | |
301 | template<typename StringType> |
302 | class StringTypeAdapter { |
303 | }; |
304 | |
305 | template<> |
306 | class StringTypeAdapter<char*> { |
307 | public: |
308 | StringTypeAdapter<char*>(char* buffer) |
309 | : m_buffer((unsigned char*)buffer) |
310 | , m_length(strlen(s: buffer)) |
311 | { |
312 | } |
313 | |
314 | unsigned length() { return m_length; } |
315 | |
316 | void writeTo(UChar* destination) |
317 | { |
318 | for (unsigned i = 0; i < m_length; ++i) |
319 | destination[i] = m_buffer[i]; |
320 | } |
321 | |
322 | private: |
323 | const unsigned char* m_buffer; |
324 | unsigned m_length; |
325 | }; |
326 | |
327 | template<> |
328 | class StringTypeAdapter<const char*> { |
329 | public: |
330 | StringTypeAdapter<const char*>(const char* buffer) |
331 | : m_buffer((unsigned char*)buffer) |
332 | , m_length(strlen(s: buffer)) |
333 | { |
334 | } |
335 | |
336 | unsigned length() { return m_length; } |
337 | |
338 | void writeTo(UChar* destination) |
339 | { |
340 | for (unsigned i = 0; i < m_length; ++i) |
341 | destination[i] = m_buffer[i]; |
342 | } |
343 | |
344 | private: |
345 | const unsigned char* m_buffer; |
346 | unsigned m_length; |
347 | }; |
348 | |
349 | template<> |
350 | class StringTypeAdapter<UString> { |
351 | public: |
352 | StringTypeAdapter<UString>(UString& string) |
353 | : m_data(string.data()) |
354 | , m_length(string.size()) |
355 | { |
356 | } |
357 | |
358 | unsigned length() { return m_length; } |
359 | |
360 | void writeTo(UChar* destination) |
361 | { |
362 | for (unsigned i = 0; i < m_length; ++i) |
363 | destination[i] = m_data[i]; |
364 | } |
365 | |
366 | private: |
367 | const UChar* m_data; |
368 | unsigned m_length; |
369 | }; |
370 | |
371 | template<typename StringType1, typename StringType2> |
372 | UString makeString(StringType1 string1, StringType2 string2) |
373 | { |
374 | StringTypeAdapter<StringType1> adapter1(string1); |
375 | StringTypeAdapter<StringType2> adapter2(string2); |
376 | |
377 | UChar* buffer; |
378 | unsigned length = adapter1.length() + adapter2.length(); |
379 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
380 | if (!resultImpl) |
381 | return UString(); |
382 | |
383 | UChar* result = buffer; |
384 | adapter1.writeTo(result); |
385 | result += adapter1.length(); |
386 | adapter2.writeTo(result); |
387 | |
388 | return resultImpl; |
389 | } |
390 | |
391 | template<typename StringType1, typename StringType2, typename StringType3> |
392 | UString makeString(StringType1 string1, StringType2 string2, StringType3 string3) |
393 | { |
394 | StringTypeAdapter<StringType1> adapter1(string1); |
395 | StringTypeAdapter<StringType2> adapter2(string2); |
396 | StringTypeAdapter<StringType3> adapter3(string3); |
397 | |
398 | UChar* buffer; |
399 | unsigned length = adapter1.length() + adapter2.length() + adapter3.length(); |
400 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
401 | if (!resultImpl) |
402 | return UString(); |
403 | |
404 | UChar* result = buffer; |
405 | adapter1.writeTo(result); |
406 | result += adapter1.length(); |
407 | adapter2.writeTo(result); |
408 | result += adapter2.length(); |
409 | adapter3.writeTo(result); |
410 | |
411 | return resultImpl; |
412 | } |
413 | |
414 | template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> |
415 | UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) |
416 | { |
417 | StringTypeAdapter<StringType1> adapter1(string1); |
418 | StringTypeAdapter<StringType2> adapter2(string2); |
419 | StringTypeAdapter<StringType3> adapter3(string3); |
420 | StringTypeAdapter<StringType4> adapter4(string4); |
421 | |
422 | UChar* buffer; |
423 | unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length(); |
424 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
425 | if (!resultImpl) |
426 | return UString(); |
427 | |
428 | UChar* result = buffer; |
429 | adapter1.writeTo(result); |
430 | result += adapter1.length(); |
431 | adapter2.writeTo(result); |
432 | result += adapter2.length(); |
433 | adapter3.writeTo(result); |
434 | result += adapter3.length(); |
435 | adapter4.writeTo(result); |
436 | |
437 | return resultImpl; |
438 | } |
439 | |
440 | template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> |
441 | UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) |
442 | { |
443 | StringTypeAdapter<StringType1> adapter1(string1); |
444 | StringTypeAdapter<StringType2> adapter2(string2); |
445 | StringTypeAdapter<StringType3> adapter3(string3); |
446 | StringTypeAdapter<StringType4> adapter4(string4); |
447 | StringTypeAdapter<StringType5> adapter5(string5); |
448 | |
449 | UChar* buffer; |
450 | unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length(); |
451 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
452 | if (!resultImpl) |
453 | return UString(); |
454 | |
455 | UChar* result = buffer; |
456 | adapter1.writeTo(result); |
457 | result += adapter1.length(); |
458 | adapter2.writeTo(result); |
459 | result += adapter2.length(); |
460 | adapter3.writeTo(result); |
461 | result += adapter3.length(); |
462 | adapter4.writeTo(result); |
463 | result += adapter4.length(); |
464 | adapter5.writeTo(result); |
465 | |
466 | return resultImpl; |
467 | } |
468 | |
469 | template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> |
470 | UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) |
471 | { |
472 | StringTypeAdapter<StringType1> adapter1(string1); |
473 | StringTypeAdapter<StringType2> adapter2(string2); |
474 | StringTypeAdapter<StringType3> adapter3(string3); |
475 | StringTypeAdapter<StringType4> adapter4(string4); |
476 | StringTypeAdapter<StringType5> adapter5(string5); |
477 | StringTypeAdapter<StringType6> adapter6(string6); |
478 | |
479 | UChar* buffer; |
480 | unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length(); |
481 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
482 | if (!resultImpl) |
483 | return UString(); |
484 | |
485 | UChar* result = buffer; |
486 | adapter1.writeTo(result); |
487 | result += adapter1.length(); |
488 | adapter2.writeTo(result); |
489 | result += adapter2.length(); |
490 | adapter3.writeTo(result); |
491 | result += adapter3.length(); |
492 | adapter4.writeTo(result); |
493 | result += adapter4.length(); |
494 | adapter5.writeTo(result); |
495 | result += adapter5.length(); |
496 | adapter6.writeTo(result); |
497 | |
498 | return resultImpl; |
499 | } |
500 | |
501 | template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> |
502 | UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) |
503 | { |
504 | StringTypeAdapter<StringType1> adapter1(string1); |
505 | StringTypeAdapter<StringType2> adapter2(string2); |
506 | StringTypeAdapter<StringType3> adapter3(string3); |
507 | StringTypeAdapter<StringType4> adapter4(string4); |
508 | StringTypeAdapter<StringType5> adapter5(string5); |
509 | StringTypeAdapter<StringType6> adapter6(string6); |
510 | StringTypeAdapter<StringType7> adapter7(string7); |
511 | |
512 | UChar* buffer; |
513 | unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length() + adapter7.length(); |
514 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
515 | if (!resultImpl) |
516 | return UString(); |
517 | |
518 | UChar* result = buffer; |
519 | adapter1.writeTo(result); |
520 | result += adapter1.length(); |
521 | adapter2.writeTo(result); |
522 | result += adapter2.length(); |
523 | adapter3.writeTo(result); |
524 | result += adapter3.length(); |
525 | adapter4.writeTo(result); |
526 | result += adapter4.length(); |
527 | adapter5.writeTo(result); |
528 | result += adapter5.length(); |
529 | adapter6.writeTo(result); |
530 | result += adapter6.length(); |
531 | adapter7.writeTo(result); |
532 | |
533 | return resultImpl; |
534 | } |
535 | |
536 | template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> |
537 | UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) |
538 | { |
539 | StringTypeAdapter<StringType1> adapter1(string1); |
540 | StringTypeAdapter<StringType2> adapter2(string2); |
541 | StringTypeAdapter<StringType3> adapter3(string3); |
542 | StringTypeAdapter<StringType4> adapter4(string4); |
543 | StringTypeAdapter<StringType5> adapter5(string5); |
544 | StringTypeAdapter<StringType6> adapter6(string6); |
545 | StringTypeAdapter<StringType7> adapter7(string7); |
546 | StringTypeAdapter<StringType8> adapter8(string8); |
547 | |
548 | UChar* buffer; |
549 | unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length() + adapter7.length() + adapter8.length(); |
550 | PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, output&: buffer); |
551 | if (!resultImpl) |
552 | return UString(); |
553 | |
554 | UChar* result = buffer; |
555 | adapter1.writeTo(result); |
556 | result += adapter1.length(); |
557 | adapter2.writeTo(result); |
558 | result += adapter2.length(); |
559 | adapter3.writeTo(result); |
560 | result += adapter3.length(); |
561 | adapter4.writeTo(result); |
562 | result += adapter4.length(); |
563 | adapter5.writeTo(result); |
564 | result += adapter5.length(); |
565 | adapter6.writeTo(result); |
566 | result += adapter6.length(); |
567 | adapter7.writeTo(result); |
568 | result += adapter7.length(); |
569 | adapter8.writeTo(result); |
570 | |
571 | return resultImpl; |
572 | } |
573 | |
574 | } // namespace JSC |
575 | |
576 | namespace WTF { |
577 | |
578 | template<typename T> struct DefaultHash; |
579 | template<typename T> struct StrHash; |
580 | |
581 | template<> struct StrHash<JSC::UString::Rep*> { |
582 | static unsigned hash(const JSC::UString::Rep* key) { return key->hash(); } |
583 | static bool equal(const JSC::UString::Rep* a, const JSC::UString::Rep* b) { return JSC::equal(a, b); } |
584 | static const bool safeToCompareToEmptyOrDeleted = false; |
585 | }; |
586 | |
587 | template<> struct StrHash<RefPtr<JSC::UString::Rep> > : public StrHash<JSC::UString::Rep*> { |
588 | using StrHash<JSC::UString::Rep*>::hash; |
589 | static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->hash(); } |
590 | using StrHash<JSC::UString::Rep*>::equal; |
591 | static bool equal(const RefPtr<JSC::UString::Rep>& a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a.get(), b.get()); } |
592 | static bool equal(const JSC::UString::Rep* a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a, b.get()); } |
593 | static bool equal(const RefPtr<JSC::UString::Rep>& a, const JSC::UString::Rep* b) { return JSC::equal(a.get(), b); } |
594 | |
595 | static const bool safeToCompareToEmptyOrDeleted = false; |
596 | }; |
597 | |
598 | template<> struct DefaultHash<JSC::UString::Rep*> { |
599 | typedef StrHash<JSC::UString::Rep*> Hash; |
600 | }; |
601 | |
602 | template<> struct DefaultHash<RefPtr<JSC::UString::Rep> > { |
603 | typedef StrHash<RefPtr<JSC::UString::Rep> > Hash; |
604 | |
605 | }; |
606 | |
607 | } // namespace WTF |
608 | |
609 | #endif |
610 | |