source: webkit/trunk/JavaScriptCore/kjs/ustring.cpp@ 34852

Last change on this file since 34852 was 34821, checked in by Darin Adler, 17 years ago

2008-06-26 Darin Adler <Darin Adler>

Reviewed by Geoff.

  • optimize UString append and the replace function a bit

SunSpider says 1.8% faster.

  • VM/JSPropertyNameIterator.cpp: Added include of JSString.h, now needed because jsString returns a JSString*.
  • VM/Machine.cpp: (KJS::Machine::privateExecute): Removed the toObject call from native function calls. Also removed code to put the this value into a register.
  • kjs/BooleanObject.cpp: (KJS::booleanProtoFuncToString): Rewrite to handle false and true separately.
  • kjs/FunctionPrototype.cpp: (KJS::constructFunction): Use single-character append rather than building a string for each character.
  • kjs/JSFunction.cpp: (KJS::globalFuncUnescape): Ditto.
  • kjs/JSImmediate.cpp: (KJS::JSImmediate::prototype): Added. Gets the appropriate prototype for use with an immediate value. To be used instead of toObject when doing a get on an immediate value.
  • kjs/JSImmediate.h: Added prototype.
  • kjs/JSObject.cpp: (KJS::JSObject::toString): Tweaked formatting.
  • kjs/JSObject.h: (KJS::JSValue::get): Use prototype instead of toObject to avoid creating an object wrapper just to search for properties. This also saves an unnecessary hash table lookup since the object wrappers themselves don't have any properties.
  • kjs/JSString.h: Added toThisString and toThisJSString.
  • kjs/JSValue.cpp: (KJS::JSCell::toThisString): Added. (KJS::JSCell::toThisJSString): Added. (KJS::JSCell::getJSNumber): Added. (KJS::jsString): Changed return type to JSString*. (KJS::jsOwnedString): Ditto.
  • kjs/JSValue.h: (KJS::JSValue::toThisString): Added. (KJS::JSValue::toThisJSString): Added. (KJS::JSValue::getJSNumber): Added.
  • kjs/NumberObject.cpp: (KJS::NumberObject::getJSNumber): Added. (KJS::integer_part_noexp): Append C string directly rather than first turning it into a UString. (KJS::numberProtoFuncToString): Use getJSNumber to check if the value is a number rather than isObject(&NumberObject::info). This works for immediate numbers, number cells, and NumberObject instances. (KJS::numberProtoFuncToLocaleString): Ditto. (KJS::numberProtoFuncValueOf): Ditto. (KJS::numberProtoFuncToFixed): Ditto. (KJS::numberProtoFuncToExponential): Ditto. (KJS::numberProtoFuncToPrecision): Ditto.
  • kjs/NumberObject.h: Added getJSNumber.
  • kjs/PropertySlot.cpp: Tweaked comment.
  • kjs/internal.cpp: (KJS::JSString::toThisString): Added. (KJS::JSString::toThisJSString): Added. (KJS::JSString::getOwnPropertySlot): Changed code that searches the prototype chain to start with the string prototype and not create a string object. (KJS::JSNumberCell::toThisString): Added. (KJS::JSNumberCell::getJSNumber): Added.
  • kjs/lookup.cpp: (KJS::staticFunctionGetter): Moved here, because there's no point in having a function that's only used for a function pointer be inline. (KJS::setUpStaticFunctionSlot): New function for getStaticFunctionSlot.
  • kjs/lookup.h: (KJS::staticValueGetter): Don't mark this inline. It doesn't make sense to have a function that's only used for a function pointer be inline. (KJS::getStaticFunctionSlot): Changed to get properties from the parent first before doing any handling of functions. This is the fastest way to return the function once the initial setup is done.
  • kjs/string_object.cpp: (KJS::StringObject::getPropertyNames): Call value() instead of getString(), avoiding an unnecessary virtual function call (the call to the type() function in the implementation of the isString() function). (KJS::StringObject::toString): Added. (KJS::StringObject::toThisString): Added. (KJS::StringObject::toThisJSString): Added. (KJS::substituteBackreferences): Rewrote to use a appending algorithm instead of a the old one that tried to replace in place. (KJS::stringProtoFuncReplace): Merged this function and the replace function. Replaced the hand-rolled dynamic arrays for source ranges and replacements with Vector. (KJS::stringProtoFuncToString): Handle JSString as well as StringObject. Removed the separate valueOf implementation, since it can just share this. (KJS::stringProtoFuncCharAt): Use toThisString, which handles JSString as well as StringObject, and is slightly more efficient than the old code too. (KJS::stringProtoFuncCharCodeAt): Ditto. (KJS::stringProtoFuncConcat): Ditto. (KJS::stringProtoFuncIndexOf): Ditto. (KJS::stringProtoFuncLastIndexOf): Ditto. (KJS::stringProtoFuncMatch): Ditto. (KJS::stringProtoFuncSearch): Ditto. (KJS::stringProtoFuncSlice): Ditto. (KJS::stringProtoFuncSplit): Ditto. (KJS::stringProtoFuncSubstr): Ditto. (KJS::stringProtoFuncSubstring): Ditto. (KJS::stringProtoFuncToLowerCase): Use toThisJSString. (KJS::stringProtoFuncToUpperCase): Ditto. (KJS::stringProtoFuncToLocaleLowerCase): Ditto. (KJS::stringProtoFuncToLocaleUpperCase): Ditto. (KJS::stringProtoFuncLocaleCompare): Ditto. (KJS::stringProtoFuncBig): Use toThisString. (KJS::stringProtoFuncSmall): Ditto. (KJS::stringProtoFuncBlink): Ditto. (KJS::stringProtoFuncBold): Ditto. (KJS::stringProtoFuncFixed): Ditto. (KJS::stringProtoFuncItalics): Ditto. (KJS::stringProtoFuncStrike): Ditto. (KJS::stringProtoFuncSub): Ditto. (KJS::stringProtoFuncSup): Ditto. (KJS::stringProtoFuncFontcolor): Ditto. (KJS::stringProtoFuncFontsize): Ditto. (KJS::stringProtoFuncAnchor): Ditto. (KJS::stringProtoFuncLink): Ditto.
  • kjs/string_object.h: Added toString, toThisString, and toThisJSString.
  • kjs/ustring.cpp: (KJS::UString::append): Added a version that takes a character pointer and size, so we don't have to create a UString just to append to another UString.
  • kjs/ustring.h:
  • Property svn:eol-style set to native
File size: 33.8 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * Copyright (C) 1999-2000 Harri Porten ([email protected])
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich ([email protected])
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "ustring.h"
26
27#include "JSLock.h"
28#include "collector.h"
29#include "dtoa.h"
30#include "JSFunction.h"
31#include "identifier.h"
32#include "operations.h"
33#include <ctype.h>
34#include <float.h>
35#include <limits.h>
36#include <math.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <wtf/Assertions.h>
40#include <wtf/ASCIICType.h>
41#include <wtf/MathExtras.h>
42#include <wtf/Vector.h>
43#include <wtf/unicode/UTF8.h>
44
45#if HAVE(STRING_H)
46#include <string.h>
47#endif
48#if HAVE(STRINGS_H)
49#include <strings.h>
50#endif
51
52using namespace WTF;
53using namespace WTF::Unicode;
54using namespace std;
55
56namespace KJS {
57
58extern const double NaN;
59extern const double Inf;
60
61static inline const size_t overflowIndicator() { return std::numeric_limits<size_t>::max(); }
62static inline const size_t maxUChars() { return std::numeric_limits<size_t>::max() / sizeof(UChar); }
63
64static inline UChar* allocChars(size_t length)
65{
66 ASSERT(length);
67 if (length > maxUChars())
68 return 0;
69 return static_cast<UChar*>(fastMalloc(sizeof(UChar) * length));
70}
71
72static inline UChar* reallocChars(UChar* buffer, size_t length)
73{
74 ASSERT(length);
75 if (length > maxUChars())
76 return 0;
77 return static_cast<UChar*>(fastRealloc(buffer, sizeof(UChar) * length));
78}
79
80COMPILE_ASSERT(sizeof(UChar) == 2, uchar_is_2_bytes)
81
82CString::CString(const char *c)
83{
84 length = strlen(c);
85 data = new char[length+1];
86 memcpy(data, c, length + 1);
87}
88
89CString::CString(const char *c, size_t len)
90{
91 length = len;
92 data = new char[len+1];
93 memcpy(data, c, len);
94 data[len] = 0;
95}
96
97CString::CString(const CString &b)
98{
99 length = b.length;
100 if (b.data) {
101 data = new char[length+1];
102 memcpy(data, b.data, length + 1);
103 }
104 else
105 data = 0;
106}
107
108CString::~CString()
109{
110 delete [] data;
111}
112
113CString CString::adopt(char* c, size_t len)
114{
115 CString s;
116 s.data = c;
117 s.length = len;
118
119 return s;
120}
121
122CString &CString::append(const CString &t)
123{
124 char *n;
125 n = new char[length+t.length+1];
126 if (length)
127 memcpy(n, data, length);
128 if (t.length)
129 memcpy(n+length, t.data, t.length);
130 length += t.length;
131 n[length] = 0;
132
133 delete [] data;
134 data = n;
135
136 return *this;
137}
138
139CString &CString::operator=(const char *c)
140{
141 if (data)
142 delete [] data;
143 length = strlen(c);
144 data = new char[length+1];
145 memcpy(data, c, length + 1);
146
147 return *this;
148}
149
150CString &CString::operator=(const CString &str)
151{
152 if (this == &str)
153 return *this;
154
155 if (data)
156 delete [] data;
157 length = str.length;
158 if (str.data) {
159 data = new char[length + 1];
160 memcpy(data, str.data, length + 1);
161 }
162 else
163 data = 0;
164
165 return *this;
166}
167
168bool operator==(const CString& c1, const CString& c2)
169{
170 size_t len = c1.size();
171 return len == c2.size() && (len == 0 || memcmp(c1.c_str(), c2.c_str(), len) == 0);
172}
173
174// These static strings are immutable, except for rc, whose initial value is chosen to reduce the possibility of it becoming zero due to ref/deref not being thread-safe.
175static UChar sharedEmptyChar;
176UString::Rep UString::Rep::null = { 0, 0, INT_MAX / 2, 0, 0, &UString::Rep::null, true, 0, 0, 0, 0, 0, 0 };
177UString::Rep UString::Rep::empty = { 0, 0, INT_MAX / 2, 0, 0, &UString::Rep::empty, true, 0, &sharedEmptyChar, 0, 0, 0, 0 };
178
179static char* statBuffer = 0; // Only used for debugging via UString::ascii().
180
181PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar *d, int l)
182{
183 int sizeInBytes = l * sizeof(UChar);
184 UChar *copyD = static_cast<UChar *>(fastMalloc(sizeInBytes));
185 memcpy(copyD, d, sizeInBytes);
186
187 return create(copyD, l);
188}
189
190PassRefPtr<UString::Rep> UString::Rep::create(UChar *d, int l)
191{
192 Rep* r = new Rep;
193 r->offset = 0;
194 r->len = l;
195 r->rc = 1;
196 r->_hash = 0;
197 r->identifierTable = 0;
198 r->baseString = r;
199 r->isStatic = false;
200 r->reportedCost = 0;
201 r->buf = d;
202 r->usedCapacity = l;
203 r->capacity = l;
204 r->usedPreCapacity = 0;
205 r->preCapacity = 0;
206
207 // steal the single reference this Rep was created with
208 return adoptRef(r);
209}
210
211PassRefPtr<UString::Rep> UString::Rep::create(PassRefPtr<Rep> base, int offset, int length)
212{
213 ASSERT(base);
214
215 int baseOffset = base->offset;
216
217 base = base->baseString;
218
219 ASSERT(-(offset + baseOffset) <= base->usedPreCapacity);
220 ASSERT(offset + baseOffset + length <= base->usedCapacity);
221
222 Rep *r = new Rep;
223 r->offset = baseOffset + offset;
224 r->len = length;
225 r->rc = 1;
226 r->_hash = 0;
227 r->identifierTable = 0;
228 r->baseString = base.releaseRef();
229 r->isStatic = false;
230 r->reportedCost = 0;
231 r->buf = 0;
232 r->usedCapacity = 0;
233 r->capacity = 0;
234 r->usedPreCapacity = 0;
235 r->preCapacity = 0;
236
237 // steal the single reference this Rep was created with
238 return adoptRef(r);
239}
240
241PassRefPtr<UString::Rep> UString::Rep::createFromUTF8(const char* string)
242{
243 if (!string)
244 return &UString::Rep::null;
245
246 size_t length = strlen(string);
247 Vector<UChar, 1024> buffer(length);
248 UChar* p = buffer.data();
249 if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length))
250 return &UString::Rep::null;
251
252 return UString::Rep::createCopying(buffer.data(), p - buffer.data());
253}
254
255void UString::Rep::destroy()
256{
257 // Static null and empty strings can never be destroyed, but we cannot rely on reference counting, because ref/deref are not thread-safe.
258 if (!isStatic) {
259 if (identifierTable)
260 Identifier::remove(this);
261 if (baseString == this)
262 fastFree(buf);
263 else
264 baseString->deref();
265
266 delete this;
267 }
268}
269
270// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
271// or anything like that.
272const unsigned PHI = 0x9e3779b9U;
273
274// Paul Hsieh's SuperFastHash
275// http://www.azillionmonkeys.com/qed/hash.html
276unsigned UString::Rep::computeHash(const UChar *s, int len)
277{
278 unsigned l = len;
279 uint32_t hash = PHI;
280 uint32_t tmp;
281
282 int rem = l & 1;
283 l >>= 1;
284
285 // Main loop
286 for (; l > 0; l--) {
287 hash += s[0];
288 tmp = (s[1] << 11) ^ hash;
289 hash = (hash << 16) ^ tmp;
290 s += 2;
291 hash += hash >> 11;
292 }
293
294 // Handle end case
295 if (rem) {
296 hash += s[0];
297 hash ^= hash << 11;
298 hash += hash >> 17;
299 }
300
301 // Force "avalanching" of final 127 bits
302 hash ^= hash << 3;
303 hash += hash >> 5;
304 hash ^= hash << 2;
305 hash += hash >> 15;
306 hash ^= hash << 10;
307
308 // this avoids ever returning a hash code of 0, since that is used to
309 // signal "hash not computed yet", using a value that is likely to be
310 // effectively the same as 0 when the low bits are masked
311 if (hash == 0)
312 hash = 0x80000000;
313
314 return hash;
315}
316
317// Paul Hsieh's SuperFastHash
318// http://www.azillionmonkeys.com/qed/hash.html
319unsigned UString::Rep::computeHash(const char *s)
320{
321 // This hash is designed to work on 16-bit chunks at a time. But since the normal case
322 // (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
323 // were 16-bit chunks, which should give matching results
324
325 uint32_t hash = PHI;
326 uint32_t tmp;
327 size_t l = strlen(s);
328
329 size_t rem = l & 1;
330 l >>= 1;
331
332 // Main loop
333 for (; l > 0; l--) {
334 hash += (unsigned char)s[0];
335 tmp = ((unsigned char)s[1] << 11) ^ hash;
336 hash = (hash << 16) ^ tmp;
337 s += 2;
338 hash += hash >> 11;
339 }
340
341 // Handle end case
342 if (rem) {
343 hash += (unsigned char)s[0];
344 hash ^= hash << 11;
345 hash += hash >> 17;
346 }
347
348 // Force "avalanching" of final 127 bits
349 hash ^= hash << 3;
350 hash += hash >> 5;
351 hash ^= hash << 2;
352 hash += hash >> 15;
353 hash ^= hash << 10;
354
355 // this avoids ever returning a hash code of 0, since that is used to
356 // signal "hash not computed yet", using a value that is likely to be
357 // effectively the same as 0 when the low bits are masked
358 if (hash == 0)
359 hash = 0x80000000;
360
361 return hash;
362}
363
364// put these early so they can be inlined
365inline size_t UString::expandedSize(size_t size, size_t otherSize) const
366{
367 // Do the size calculation in two parts, returning overflowIndicator if
368 // we overflow the maximum value that we can handle.
369
370 if (size > maxUChars())
371 return overflowIndicator();
372
373 size_t expandedSize = ((size + 10) / 10 * 11) + 1;
374 if (maxUChars() - expandedSize < otherSize)
375 return overflowIndicator();
376
377 return expandedSize + otherSize;
378}
379
380inline int UString::usedCapacity() const
381{
382 return m_rep->baseString->usedCapacity;
383}
384
385inline int UString::usedPreCapacity() const
386{
387 return m_rep->baseString->usedPreCapacity;
388}
389
390void UString::expandCapacity(int requiredLength)
391{
392 Rep* r = m_rep->baseString;
393
394 if (requiredLength > r->capacity) {
395 size_t newCapacity = expandedSize(requiredLength, r->preCapacity);
396 UChar* oldBuf = r->buf;
397 r->buf = reallocChars(r->buf, newCapacity);
398 if (!r->buf) {
399 r->buf = oldBuf;
400 m_rep = &Rep::null;
401 return;
402 }
403 r->capacity = newCapacity - r->preCapacity;
404 }
405 if (requiredLength > r->usedCapacity) {
406 r->usedCapacity = requiredLength;
407 }
408}
409
410void UString::expandPreCapacity(int requiredPreCap)
411{
412 Rep* r = m_rep->baseString;
413
414 if (requiredPreCap > r->preCapacity) {
415 size_t newCapacity = expandedSize(requiredPreCap, r->capacity);
416 int delta = newCapacity - r->capacity - r->preCapacity;
417
418 UChar* newBuf = allocChars(newCapacity);
419 if (!newBuf) {
420 m_rep = &Rep::null;
421 return;
422 }
423 memcpy(newBuf + delta, r->buf, (r->capacity + r->preCapacity) * sizeof(UChar));
424 fastFree(r->buf);
425 r->buf = newBuf;
426
427 r->preCapacity = newCapacity - r->capacity;
428 }
429 if (requiredPreCap > r->usedPreCapacity) {
430 r->usedPreCapacity = requiredPreCap;
431 }
432}
433
434UString::UString(const char *c)
435{
436 if (!c) {
437 m_rep = &Rep::null;
438 return;
439 }
440
441 if (!c[0]) {
442 m_rep = &Rep::empty;
443 return;
444 }
445
446 size_t length = strlen(c);
447 UChar *d = allocChars(length);
448 if (!d)
449 m_rep = &Rep::null;
450 else {
451 for (size_t i = 0; i < length; i++)
452 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
453 m_rep = Rep::create(d, static_cast<int>(length));
454 }
455}
456
457UString::UString(const UChar *c, int length)
458{
459 if (length == 0)
460 m_rep = &Rep::empty;
461 else
462 m_rep = Rep::createCopying(c, length);
463}
464
465UString::UString(UChar *c, int length, bool copy)
466{
467 if (length == 0)
468 m_rep = &Rep::empty;
469 else if (copy)
470 m_rep = Rep::createCopying(c, length);
471 else
472 m_rep = Rep::create(c, length);
473}
474
475UString::UString(const Vector<UChar>& buffer)
476{
477 if (!buffer.size())
478 m_rep = &Rep::empty;
479 else
480 m_rep = Rep::createCopying(buffer.data(), buffer.size());
481}
482
483
484UString::UString(const UString &a, const UString &b)
485{
486 int aSize = a.size();
487 int aOffset = a.m_rep->offset;
488 int bSize = b.size();
489 int bOffset = b.m_rep->offset;
490 int length = aSize + bSize;
491
492 // possible cases:
493
494 if (aSize == 0) {
495 // a is empty
496 m_rep = b.m_rep;
497 } else if (bSize == 0) {
498 // b is empty
499 m_rep = a.m_rep;
500 } else if (aOffset + aSize == a.usedCapacity() && aSize >= minShareSize && 4 * aSize >= bSize &&
501 (-bOffset != b.usedPreCapacity() || aSize >= bSize)) {
502 // - a reaches the end of its buffer so it qualifies for shared append
503 // - also, it's at least a quarter the length of b - appending to a much shorter
504 // string does more harm than good
505 // - however, if b qualifies for prepend and is longer than a, we'd rather prepend
506 UString x(a);
507 x.expandCapacity(aOffset + length);
508 if (a.data() && x.data()) {
509 memcpy(const_cast<UChar *>(a.data() + aSize), b.data(), bSize * sizeof(UChar));
510 m_rep = Rep::create(a.m_rep, 0, length);
511 } else
512 m_rep = &Rep::null;
513 } else if (-bOffset == b.usedPreCapacity() && bSize >= minShareSize && 4 * bSize >= aSize) {
514 // - b reaches the beginning of its buffer so it qualifies for shared prepend
515 // - also, it's at least a quarter the length of a - prepending to a much shorter
516 // string does more harm than good
517 UString y(b);
518 y.expandPreCapacity(-bOffset + aSize);
519 if (b.data() && y.data()) {
520 memcpy(const_cast<UChar *>(b.data() - aSize), a.data(), aSize * sizeof(UChar));
521 m_rep = Rep::create(b.m_rep, -aSize, length);
522 } else
523 m_rep = &Rep::null;
524 } else {
525 // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string
526 size_t newCapacity = expandedSize(length, 0);
527 UChar* d = allocChars(newCapacity);
528 if (!d)
529 m_rep = &Rep::null;
530 else {
531 memcpy(d, a.data(), aSize * sizeof(UChar));
532 memcpy(d + aSize, b.data(), bSize * sizeof(UChar));
533 m_rep = Rep::create(d, length);
534 m_rep->capacity = newCapacity;
535 }
536 }
537}
538
539const UString& UString::null()
540{
541 static UString* n = new UString; // Should be called from main thread at least once to be safely initialized.
542 return *n;
543}
544
545UString UString::from(int i)
546{
547 UChar buf[1 + sizeof(i) * 3];
548 UChar *end = buf + sizeof(buf) / sizeof(UChar);
549 UChar *p = end;
550
551 if (i == 0) {
552 *--p = '0';
553 } else if (i == INT_MIN) {
554 char minBuf[1 + sizeof(i) * 3];
555 sprintf(minBuf, "%d", INT_MIN);
556 return UString(minBuf);
557 } else {
558 bool negative = false;
559 if (i < 0) {
560 negative = true;
561 i = -i;
562 }
563 while (i) {
564 *--p = (unsigned short)((i % 10) + '0');
565 i /= 10;
566 }
567 if (negative) {
568 *--p = '-';
569 }
570 }
571
572 return UString(p, static_cast<int>(end - p));
573}
574
575UString UString::from(unsigned int u)
576{
577 UChar buf[sizeof(u) * 3];
578 UChar *end = buf + sizeof(buf) / sizeof(UChar);
579 UChar *p = end;
580
581 if (u == 0) {
582 *--p = '0';
583 } else {
584 while (u) {
585 *--p = (unsigned short)((u % 10) + '0');
586 u /= 10;
587 }
588 }
589
590 return UString(p, static_cast<int>(end - p));
591}
592
593UString UString::from(long l)
594{
595 UChar buf[1 + sizeof(l) * 3];
596 UChar *end = buf + sizeof(buf) / sizeof(UChar);
597 UChar *p = end;
598
599 if (l == 0) {
600 *--p = '0';
601 } else if (l == LONG_MIN) {
602 char minBuf[1 + sizeof(l) * 3];
603 sprintf(minBuf, "%ld", LONG_MIN);
604 return UString(minBuf);
605 } else {
606 bool negative = false;
607 if (l < 0) {
608 negative = true;
609 l = -l;
610 }
611 while (l) {
612 *--p = (unsigned short)((l % 10) + '0');
613 l /= 10;
614 }
615 if (negative) {
616 *--p = '-';
617 }
618 }
619
620 return UString(p, static_cast<int>(end - p));
621}
622
623UString UString::from(double d)
624{
625 // avoid ever printing -NaN, in JS conceptually there is only one NaN value
626 if (isnan(d))
627 return "NaN";
628
629 char buf[80];
630 int decimalPoint;
631 int sign;
632
633 char *result = dtoa(d, 0, &decimalPoint, &sign, NULL);
634 int length = static_cast<int>(strlen(result));
635
636 int i = 0;
637 if (sign) {
638 buf[i++] = '-';
639 }
640
641 if (decimalPoint <= 0 && decimalPoint > -6) {
642 buf[i++] = '0';
643 buf[i++] = '.';
644 for (int j = decimalPoint; j < 0; j++) {
645 buf[i++] = '0';
646 }
647 strcpy(buf + i, result);
648 } else if (decimalPoint <= 21 && decimalPoint > 0) {
649 if (length <= decimalPoint) {
650 strcpy(buf + i, result);
651 i += length;
652 for (int j = 0; j < decimalPoint - length; j++) {
653 buf[i++] = '0';
654 }
655 buf[i] = '\0';
656 } else {
657 strncpy(buf + i, result, decimalPoint);
658 i += decimalPoint;
659 buf[i++] = '.';
660 strcpy(buf + i, result + decimalPoint);
661 }
662 } else if (result[0] < '0' || result[0] > '9') {
663 strcpy(buf + i, result);
664 } else {
665 buf[i++] = result[0];
666 if (length > 1) {
667 buf[i++] = '.';
668 strcpy(buf + i, result + 1);
669 i += length - 1;
670 }
671
672 buf[i++] = 'e';
673 buf[i++] = (decimalPoint >= 0) ? '+' : '-';
674 // decimalPoint can't be more than 3 digits decimal given the
675 // nature of float representation
676 int exponential = decimalPoint - 1;
677 if (exponential < 0)
678 exponential = -exponential;
679 if (exponential >= 100)
680 buf[i++] = static_cast<char>('0' + exponential / 100);
681 if (exponential >= 10)
682 buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
683 buf[i++] = static_cast<char>('0' + exponential % 10);
684 buf[i++] = '\0';
685 }
686
687 freedtoa(result);
688
689 return UString(buf);
690}
691
692UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const
693{
694 if (rangeCount == 1 && separatorCount == 0) {
695 int thisSize = size();
696 int position = substringRanges[0].position;
697 int length = substringRanges[0].length;
698 if (position <= 0 && length >= thisSize)
699 return *this;
700 return UString::Rep::create(m_rep, max(0, position), min(thisSize, length));
701 }
702
703 int totalLength = 0;
704 for (int i = 0; i < rangeCount; i++)
705 totalLength += substringRanges[i].length;
706 for (int i = 0; i < separatorCount; i++)
707 totalLength += separators[i].size();
708
709 if (totalLength == 0)
710 return "";
711
712 UChar* buffer = allocChars(totalLength);
713 if (!buffer)
714 return null();
715
716 int maxCount = max(rangeCount, separatorCount);
717 int bufferPos = 0;
718 for (int i = 0; i < maxCount; i++) {
719 if (i < rangeCount) {
720 memcpy(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length * sizeof(UChar));
721 bufferPos += substringRanges[i].length;
722 }
723 if (i < separatorCount) {
724 memcpy(buffer + bufferPos, separators[i].data(), separators[i].size() * sizeof(UChar));
725 bufferPos += separators[i].size();
726 }
727 }
728
729 return UString::Rep::create(buffer, totalLength);
730}
731
732UString& UString::append(const UString &t)
733{
734 int thisSize = size();
735 int thisOffset = m_rep->offset;
736 int tSize = t.size();
737 int length = thisSize + tSize;
738
739 // possible cases:
740 if (thisSize == 0) {
741 // this is empty
742 *this = t;
743 } else if (tSize == 0) {
744 // t is empty
745 } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
746 // this is direct and has refcount of 1 (so we can just alter it directly)
747 expandCapacity(thisOffset + length);
748 if (data()) {
749 memcpy(const_cast<UChar*>(data() + thisSize), t.data(), tSize * sizeof(UChar));
750 m_rep->len = length;
751 m_rep->_hash = 0;
752 }
753 } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) {
754 // this reaches the end of the buffer - extend it if it's long enough to append to
755 expandCapacity(thisOffset + length);
756 if (data()) {
757 memcpy(const_cast<UChar*>(data() + thisSize), t.data(), tSize * sizeof(UChar));
758 m_rep = Rep::create(m_rep, 0, length);
759 }
760 } else {
761 // this is shared with someone using more capacity, gotta make a whole new string
762 size_t newCapacity = expandedSize(length, 0);
763 UChar* d = allocChars(newCapacity);
764 if (!d)
765 m_rep = &Rep::null;
766 else {
767 memcpy(d, data(), thisSize * sizeof(UChar));
768 memcpy(const_cast<UChar*>(d + thisSize), t.data(), tSize * sizeof(UChar));
769 m_rep = Rep::create(d, length);
770 m_rep->capacity = newCapacity;
771 }
772 }
773
774 return *this;
775}
776
777UString& UString::append(const UChar* tData, int tSize)
778{
779 int thisSize = size();
780 int thisOffset = m_rep->offset;
781 int length = thisSize + tSize;
782
783 // possible cases:
784 if (tSize == 0) {
785 // t is empty
786 } else if (thisSize == 0) {
787 // this is empty
788 m_rep = Rep::createCopying(tData, tSize);
789 } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
790 // this is direct and has refcount of 1 (so we can just alter it directly)
791 expandCapacity(thisOffset + length);
792 if (data()) {
793 memcpy(const_cast<UChar*>(data() + thisSize), tData, tSize * sizeof(UChar));
794 m_rep->len = length;
795 m_rep->_hash = 0;
796 }
797 } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) {
798 // this reaches the end of the buffer - extend it if it's long enough to append to
799 expandCapacity(thisOffset + length);
800 if (data()) {
801 memcpy(const_cast<UChar*>(data() + thisSize), tData, tSize * sizeof(UChar));
802 m_rep = Rep::create(m_rep, 0, length);
803 }
804 } else {
805 // this is shared with someone using more capacity, gotta make a whole new string
806 size_t newCapacity = expandedSize(length, 0);
807 UChar* d = allocChars(newCapacity);
808 if (!d)
809 m_rep = &Rep::null;
810 else {
811 memcpy(d, data(), thisSize * sizeof(UChar));
812 memcpy(const_cast<UChar*>(d + thisSize), tData, tSize * sizeof(UChar));
813 m_rep = Rep::create(d, length);
814 m_rep->capacity = newCapacity;
815 }
816 }
817
818 return *this;
819}
820
821UString& UString::append(const char *t)
822{
823 int thisSize = size();
824 int thisOffset = m_rep->offset;
825 int tSize = static_cast<int>(strlen(t));
826 int length = thisSize + tSize;
827
828 // possible cases:
829 if (thisSize == 0) {
830 // this is empty
831 *this = t;
832 } else if (tSize == 0) {
833 // t is empty, we'll just return *this below.
834 } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
835 // this is direct and has refcount of 1 (so we can just alter it directly)
836 expandCapacity(thisOffset + length);
837 UChar *d = const_cast<UChar *>(data());
838 if (d) {
839 for (int i = 0; i < tSize; ++i)
840 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
841 m_rep->len = length;
842 m_rep->_hash = 0;
843 }
844 } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) {
845 // this string reaches the end of the buffer - extend it
846 expandCapacity(thisOffset + length);
847 UChar *d = const_cast<UChar *>(data());
848 if (d) {
849 for (int i = 0; i < tSize; ++i)
850 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
851 m_rep = Rep::create(m_rep, 0, length);
852 }
853 } else {
854 // this is shared with someone using more capacity, gotta make a whole new string
855 size_t newCapacity = expandedSize(length, 0);
856 UChar* d = allocChars(newCapacity);
857 if (!d)
858 m_rep = &Rep::null;
859 else {
860 memcpy(d, data(), thisSize * sizeof(UChar));
861 for (int i = 0; i < tSize; ++i)
862 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
863 m_rep = Rep::create(d, length);
864 m_rep->capacity = newCapacity;
865 }
866 }
867
868 return *this;
869}
870
871UString& UString::append(UChar c)
872{
873 int thisOffset = m_rep->offset;
874 int length = size();
875
876 // possible cases:
877 if (length == 0) {
878 // this is empty - must make a new m_rep because we don't want to pollute the shared empty one
879 size_t newCapacity = expandedSize(1, 0);
880 UChar* d = allocChars(newCapacity);
881 if (!d)
882 m_rep = &Rep::null;
883 else {
884 d[0] = c;
885 m_rep = Rep::create(d, 1);
886 m_rep->capacity = newCapacity;
887 }
888 } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
889 // this is direct and has refcount of 1 (so we can just alter it directly)
890 expandCapacity(thisOffset + length + 1);
891 UChar *d = const_cast<UChar *>(data());
892 if (d) {
893 d[length] = c;
894 m_rep->len = length + 1;
895 m_rep->_hash = 0;
896 }
897 } else if (thisOffset + length == usedCapacity() && length >= minShareSize) {
898 // this reaches the end of the string - extend it and share
899 expandCapacity(thisOffset + length + 1);
900 UChar *d = const_cast<UChar *>(data());
901 if (d) {
902 d[length] = c;
903 m_rep = Rep::create(m_rep, 0, length + 1);
904 }
905 } else {
906 // this is shared with someone using more capacity, gotta make a whole new string
907 size_t newCapacity = expandedSize(length + 1, 0);
908 UChar* d = allocChars(newCapacity);
909 if (!d)
910 m_rep = &Rep::null;
911 else {
912 memcpy(d, data(), length * sizeof(UChar));
913 d[length] = c;
914 m_rep = Rep::create(d, length + 1);
915 m_rep->capacity = newCapacity;
916 }
917 }
918
919 return *this;
920}
921
922bool UString::getCString(CStringBuffer& buffer) const
923{
924 int length = size();
925 int neededSize = length + 1;
926 buffer.resize(neededSize);
927 char* buf = buffer.data();
928
929 UChar ored = 0;
930 const UChar* p = data();
931 char* q = buf;
932 const UChar* limit = p + length;
933 while (p != limit) {
934 UChar c = p[0];
935 ored |= c;
936 *q = static_cast<char>(c);
937 ++p;
938 ++q;
939 }
940 *q = '\0';
941
942 return !(ored & 0xFF00);
943}
944
945char *UString::ascii() const
946{
947 int length = size();
948 int neededSize = length + 1;
949 delete[] statBuffer;
950 statBuffer = new char[neededSize];
951
952 const UChar *p = data();
953 char *q = statBuffer;
954 const UChar *limit = p + length;
955 while (p != limit) {
956 *q = static_cast<char>(p[0]);
957 ++p;
958 ++q;
959 }
960 *q = '\0';
961
962 return statBuffer;
963}
964
965UString& UString::operator=(const char *c)
966{
967 if (!c) {
968 m_rep = &Rep::null;
969 return *this;
970 }
971
972 if (!c[0]) {
973 m_rep = &Rep::empty;
974 return *this;
975 }
976
977 int l = static_cast<int>(strlen(c));
978 UChar *d;
979 if (m_rep->rc == 1 && l <= m_rep->capacity && m_rep->baseIsSelf() && m_rep->offset == 0 && m_rep->preCapacity == 0) {
980 d = m_rep->buf;
981 m_rep->_hash = 0;
982 m_rep->len = l;
983 } else {
984 d = allocChars(l);
985 if (!d) {
986 m_rep = &Rep::null;
987 return *this;
988 }
989 m_rep = Rep::create(d, l);
990 }
991 for (int i = 0; i < l; i++)
992 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
993
994 return *this;
995}
996
997bool UString::is8Bit() const
998{
999 const UChar *u = data();
1000 const UChar *limit = u + size();
1001 while (u < limit) {
1002 if (u[0] > 0xFF)
1003 return false;
1004 ++u;
1005 }
1006
1007 return true;
1008}
1009
1010UChar UString::operator[](int pos) const
1011{
1012 if (pos >= size())
1013 return '\0';
1014 return data()[pos];
1015}
1016
1017double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
1018{
1019 double d;
1020
1021 // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk
1022 // after the number, so this is too strict a check.
1023 CStringBuffer s;
1024 if (!getCString(s))
1025 return NaN;
1026 const char* c = s.data();
1027
1028 // skip leading white space
1029 while (isASCIISpace(*c))
1030 c++;
1031
1032 // empty string ?
1033 if (*c == '\0')
1034 return tolerateEmptyString ? 0.0 : NaN;
1035
1036 // hex number ?
1037 if (*c == '0' && (*(c+1) == 'x' || *(c+1) == 'X')) {
1038 const char* firstDigitPosition = c + 2;
1039 c++;
1040 d = 0.0;
1041 while (*(++c)) {
1042 if (*c >= '0' && *c <= '9')
1043 d = d * 16.0 + *c - '0';
1044 else if ((*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f'))
1045 d = d * 16.0 + (*c & 0xdf) - 'A' + 10.0;
1046 else
1047 break;
1048 }
1049
1050 if (d >= mantissaOverflowLowerBound)
1051 d = parseIntOverflow(firstDigitPosition, c - firstDigitPosition, 16);
1052 } else {
1053 // regular number ?
1054 char *end;
1055 d = strtod(c, &end);
1056 if ((d != 0.0 || end != c) && d != Inf && d != -Inf) {
1057 c = end;
1058 } else {
1059 double sign = 1.0;
1060
1061 if (*c == '+')
1062 c++;
1063 else if (*c == '-') {
1064 sign = -1.0;
1065 c++;
1066 }
1067
1068 // We used strtod() to do the conversion. However, strtod() handles
1069 // infinite values slightly differently than JavaScript in that it
1070 // converts the string "inf" with any capitalization to infinity,
1071 // whereas the ECMA spec requires that it be converted to NaN.
1072
1073 if (c[0] == 'I' && c[1] == 'n' && c[2] == 'f' && c[3] == 'i' && c[4] == 'n' && c[5] == 'i' && c[6] == 't' && c[7] == 'y') {
1074 d = sign * Inf;
1075 c += 8;
1076 } else if ((d == Inf || d == -Inf) && *c != 'I' && *c != 'i')
1077 c = end;
1078 else
1079 return NaN;
1080 }
1081 }
1082
1083 // allow trailing white space
1084 while (isASCIISpace(*c))
1085 c++;
1086 // don't allow anything after - unless tolerant=true
1087 if (!tolerateTrailingJunk && *c != '\0')
1088 d = NaN;
1089
1090 return d;
1091}
1092
1093double UString::toDouble(bool tolerateTrailingJunk) const
1094{
1095 return toDouble(tolerateTrailingJunk, true);
1096}
1097
1098double UString::toDouble() const
1099{
1100 return toDouble(false, true);
1101}
1102
1103uint32_t UString::toUInt32(bool *ok) const
1104{
1105 double d = toDouble();
1106 bool b = true;
1107
1108 if (d != static_cast<uint32_t>(d)) {
1109 b = false;
1110 d = 0;
1111 }
1112
1113 if (ok)
1114 *ok = b;
1115
1116 return static_cast<uint32_t>(d);
1117}
1118
1119uint32_t UString::toUInt32(bool *ok, bool tolerateEmptyString) const
1120{
1121 double d = toDouble(false, tolerateEmptyString);
1122 bool b = true;
1123
1124 if (d != static_cast<uint32_t>(d)) {
1125 b = false;
1126 d = 0;
1127 }
1128
1129 if (ok)
1130 *ok = b;
1131
1132 return static_cast<uint32_t>(d);
1133}
1134
1135uint32_t UString::toStrictUInt32(bool *ok) const
1136{
1137 if (ok)
1138 *ok = false;
1139
1140 // Empty string is not OK.
1141 int len = m_rep->len;
1142 if (len == 0)
1143 return 0;
1144 const UChar *p = m_rep->data();
1145 unsigned short c = p[0];
1146
1147 // If the first digit is 0, only 0 itself is OK.
1148 if (c == '0') {
1149 if (len == 1 && ok)
1150 *ok = true;
1151 return 0;
1152 }
1153
1154 // Convert to UInt32, checking for overflow.
1155 uint32_t i = 0;
1156 while (1) {
1157 // Process character, turning it into a digit.
1158 if (c < '0' || c > '9')
1159 return 0;
1160 const unsigned d = c - '0';
1161
1162 // Multiply by 10, checking for overflow out of 32 bits.
1163 if (i > 0xFFFFFFFFU / 10)
1164 return 0;
1165 i *= 10;
1166
1167 // Add in the digit, checking for overflow out of 32 bits.
1168 const unsigned max = 0xFFFFFFFFU - d;
1169 if (i > max)
1170 return 0;
1171 i += d;
1172
1173 // Handle end of string.
1174 if (--len == 0) {
1175 if (ok)
1176 *ok = true;
1177 return i;
1178 }
1179
1180 // Get next character.
1181 c = *(++p);
1182 }
1183}
1184
1185int UString::find(const UString &f, int pos) const
1186{
1187 int sz = size();
1188 int fsz = f.size();
1189 if (sz < fsz)
1190 return -1;
1191 if (pos < 0)
1192 pos = 0;
1193 if (fsz == 0)
1194 return pos;
1195 const UChar *end = data() + sz - fsz;
1196 int fsizeminusone = (fsz - 1) * sizeof(UChar);
1197 const UChar *fdata = f.data();
1198 unsigned short fchar = fdata[0];
1199 ++fdata;
1200 for (const UChar *c = data() + pos; c <= end; c++)
1201 if (c[0] == fchar && !memcmp(c + 1, fdata, fsizeminusone))
1202 return static_cast<int>(c - data());
1203
1204 return -1;
1205}
1206
1207int UString::find(UChar ch, int pos) const
1208{
1209 if (pos < 0)
1210 pos = 0;
1211 const UChar *end = data() + size();
1212 for (const UChar *c = data() + pos; c < end; c++)
1213 if (*c == ch)
1214 return static_cast<int>(c - data());
1215
1216 return -1;
1217}
1218
1219int UString::rfind(const UString &f, int pos) const
1220{
1221 int sz = size();
1222 int fsz = f.size();
1223 if (sz < fsz)
1224 return -1;
1225 if (pos < 0)
1226 pos = 0;
1227 if (pos > sz - fsz)
1228 pos = sz - fsz;
1229 if (fsz == 0)
1230 return pos;
1231 int fsizeminusone = (fsz - 1) * sizeof(UChar);
1232 const UChar *fdata = f.data();
1233 for (const UChar *c = data() + pos; c >= data(); c--) {
1234 if (*c == *fdata && !memcmp(c + 1, fdata + 1, fsizeminusone))
1235 return static_cast<int>(c - data());
1236 }
1237
1238 return -1;
1239}
1240
1241int UString::rfind(UChar ch, int pos) const
1242{
1243 if (isEmpty())
1244 return -1;
1245 if (pos + 1 >= size())
1246 pos = size() - 1;
1247 for (const UChar *c = data() + pos; c >= data(); c--) {
1248 if (*c == ch)
1249 return static_cast<int>(c-data());
1250 }
1251
1252 return -1;
1253}
1254
1255UString UString::substr(int pos, int len) const
1256{
1257 int s = size();
1258
1259 if (pos < 0)
1260 pos = 0;
1261 else if (pos >= s)
1262 pos = s;
1263 if (len < 0)
1264 len = s;
1265 if (pos + len >= s)
1266 len = s - pos;
1267
1268 if (pos == 0 && len == s)
1269 return *this;
1270
1271 return UString(Rep::create(m_rep, pos, len));
1272}
1273
1274bool operator==(const UString& s1, const UString& s2)
1275{
1276 if (s1.m_rep->len != s2.m_rep->len)
1277 return false;
1278
1279 return (memcmp(s1.m_rep->data(), s2.m_rep->data(),
1280 s1.m_rep->len * sizeof(UChar)) == 0);
1281}
1282
1283bool operator==(const UString& s1, const char *s2)
1284{
1285 if (s2 == 0) {
1286 return s1.isEmpty();
1287 }
1288
1289 const UChar *u = s1.data();
1290 const UChar *uend = u + s1.size();
1291 while (u != uend && *s2) {
1292 if (u[0] != (unsigned char)*s2)
1293 return false;
1294 s2++;
1295 u++;
1296 }
1297
1298 return u == uend && *s2 == 0;
1299}
1300
1301bool operator<(const UString& s1, const UString& s2)
1302{
1303 const int l1 = s1.size();
1304 const int l2 = s2.size();
1305 const int lmin = l1 < l2 ? l1 : l2;
1306 const UChar *c1 = s1.data();
1307 const UChar *c2 = s2.data();
1308 int l = 0;
1309 while (l < lmin && *c1 == *c2) {
1310 c1++;
1311 c2++;
1312 l++;
1313 }
1314 if (l < lmin)
1315 return (c1[0] < c2[0]);
1316
1317 return (l1 < l2);
1318}
1319
1320bool operator>(const UString& s1, const UString& s2)
1321{
1322 const int l1 = s1.size();
1323 const int l2 = s2.size();
1324 const int lmin = l1 < l2 ? l1 : l2;
1325 const UChar *c1 = s1.data();
1326 const UChar *c2 = s2.data();
1327 int l = 0;
1328 while (l < lmin && *c1 == *c2) {
1329 c1++;
1330 c2++;
1331 l++;
1332 }
1333 if (l < lmin)
1334 return (c1[0] > c2[0]);
1335
1336 return (l1 > l2);
1337}
1338
1339int compare(const UString& s1, const UString& s2)
1340{
1341 const int l1 = s1.size();
1342 const int l2 = s2.size();
1343 const int lmin = l1 < l2 ? l1 : l2;
1344 const UChar *c1 = s1.data();
1345 const UChar *c2 = s2.data();
1346 int l = 0;
1347 while (l < lmin && *c1 == *c2) {
1348 c1++;
1349 c2++;
1350 l++;
1351 }
1352
1353 if (l < lmin)
1354 return (c1[0] > c2[0]) ? 1 : -1;
1355
1356 if (l1 == l2)
1357 return 0;
1358
1359 return (l1 > l2) ? 1 : -1;
1360}
1361
1362bool equal(const UString::Rep* r, const UString::Rep* b)
1363{
1364 int length = r->len;
1365 if (length != b->len)
1366 return false;
1367 const UChar* d = r->data();
1368 const UChar* s = b->data();
1369 for (int i = 0; i != length; ++i)
1370 if (d[i] != s[i])
1371 return false;
1372 return true;
1373}
1374
1375CString UString::UTF8String(bool strict) const
1376{
1377 // Allocate a buffer big enough to hold all the characters.
1378 const int length = size();
1379 Vector<char, 1024> buffer(length * 3);
1380
1381 // Convert to runs of 8-bit characters.
1382 char* p = buffer.data();
1383 const UChar* d = reinterpret_cast<const UChar*>(&data()[0]);
1384 ConversionResult result = convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), strict);
1385 if (result != conversionOK)
1386 return CString();
1387
1388 return CString(buffer.data(), p - buffer.data());
1389}
1390
1391} // namespace KJS
Note: See TracBrowser for help on using the repository browser.