js/src/vm/String.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "vm/String-inl.h"
michael@0 8
michael@0 9 #include "mozilla/MathAlgorithms.h"
michael@0 10 #include "mozilla/MemoryReporting.h"
michael@0 11 #include "mozilla/PodOperations.h"
michael@0 12 #include "mozilla/RangedPtr.h"
michael@0 13
michael@0 14 #include "gc/Marking.h"
michael@0 15
michael@0 16 #include "jscntxtinlines.h"
michael@0 17 #include "jscompartmentinlines.h"
michael@0 18
michael@0 19 using namespace js;
michael@0 20
michael@0 21 using mozilla::PodCopy;
michael@0 22 using mozilla::RangedPtr;
michael@0 23 using mozilla::RoundUpPow2;
michael@0 24
michael@0 25 bool
michael@0 26 JSString::isFatInline() const
michael@0 27 {
michael@0 28 // It's possible for fat-inline strings to be converted to flat strings;
michael@0 29 // as a result, checking just for the arena isn't enough to determine if a
michael@0 30 // string is fat-inline. Hence the isInline() check.
michael@0 31 bool is_FatInline = (getAllocKind() == gc::FINALIZE_FAT_INLINE_STRING) && isInline();
michael@0 32 JS_ASSERT_IF(is_FatInline, isFlat());
michael@0 33 return is_FatInline;
michael@0 34 }
michael@0 35
michael@0 36 bool
michael@0 37 JSString::isExternal() const
michael@0 38 {
michael@0 39 bool is_external = (getAllocKind() == gc::FINALIZE_EXTERNAL_STRING);
michael@0 40 JS_ASSERT_IF(is_external, isFlat());
michael@0 41 return is_external;
michael@0 42 }
michael@0 43
michael@0 44 size_t
michael@0 45 JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
michael@0 46 {
michael@0 47 // JSRope: do nothing, we'll count all children chars when we hit the leaf strings.
michael@0 48 if (isRope())
michael@0 49 return 0;
michael@0 50
michael@0 51 JS_ASSERT(isLinear());
michael@0 52
michael@0 53 // JSDependentString: do nothing, we'll count the chars when we hit the base string.
michael@0 54 if (isDependent())
michael@0 55 return 0;
michael@0 56
michael@0 57 JS_ASSERT(isFlat());
michael@0 58
michael@0 59 // JSExtensibleString: count the full capacity, not just the used space.
michael@0 60 if (isExtensible()) {
michael@0 61 JSExtensibleString &extensible = asExtensible();
michael@0 62 return mallocSizeOf(extensible.chars());
michael@0 63 }
michael@0 64
michael@0 65 // JSExternalString: don't count, the chars could be stored anywhere.
michael@0 66 if (isExternal())
michael@0 67 return 0;
michael@0 68
michael@0 69 // JSInlineString, JSFatInlineString [JSInlineAtom, JSFatInlineAtom]: the chars are inline.
michael@0 70 if (isInline())
michael@0 71 return 0;
michael@0 72
michael@0 73 // JSAtom, JSUndependedString: measure the space for the chars. For
michael@0 74 // JSUndependedString, there is no need to count the base string, for the
michael@0 75 // same reason as JSDependentString above.
michael@0 76 JSFlatString &flat = asFlat();
michael@0 77 return mallocSizeOf(flat.chars());
michael@0 78 }
michael@0 79
michael@0 80 #ifdef DEBUG
michael@0 81
michael@0 82 void
michael@0 83 JSString::dumpChars(const jschar *s, size_t n)
michael@0 84 {
michael@0 85 if (n == SIZE_MAX) {
michael@0 86 n = 0;
michael@0 87 while (s[n])
michael@0 88 n++;
michael@0 89 }
michael@0 90
michael@0 91 fputc('"', stderr);
michael@0 92 for (size_t i = 0; i < n; i++) {
michael@0 93 if (s[i] == '\n')
michael@0 94 fprintf(stderr, "\\n");
michael@0 95 else if (s[i] == '\t')
michael@0 96 fprintf(stderr, "\\t");
michael@0 97 else if (s[i] >= 32 && s[i] < 127)
michael@0 98 fputc(s[i], stderr);
michael@0 99 else if (s[i] <= 255)
michael@0 100 fprintf(stderr, "\\x%02x", (unsigned int) s[i]);
michael@0 101 else
michael@0 102 fprintf(stderr, "\\u%04x", (unsigned int) s[i]);
michael@0 103 }
michael@0 104 fputc('"', stderr);
michael@0 105 }
michael@0 106
michael@0 107 void
michael@0 108 JSString::dump()
michael@0 109 {
michael@0 110 if (const jschar *chars = getChars(nullptr)) {
michael@0 111 fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
michael@0 112 (void *) this, (void *) chars);
michael@0 113 dumpChars(chars, length());
michael@0 114 } else {
michael@0 115 fprintf(stderr, "(oom in JSString::dump)");
michael@0 116 }
michael@0 117 fputc('\n', stderr);
michael@0 118 }
michael@0 119
michael@0 120 bool
michael@0 121 JSString::equals(const char *s)
michael@0 122 {
michael@0 123 const jschar *c = getChars(nullptr);
michael@0 124 if (!c) {
michael@0 125 fprintf(stderr, "OOM in JSString::equals!\n");
michael@0 126 return false;
michael@0 127 }
michael@0 128 while (*c && *s) {
michael@0 129 if (*c != *s)
michael@0 130 return false;
michael@0 131 c++;
michael@0 132 s++;
michael@0 133 }
michael@0 134 return *c == *s;
michael@0 135 }
michael@0 136 #endif /* DEBUG */
michael@0 137
michael@0 138 static MOZ_ALWAYS_INLINE bool
michael@0 139 AllocChars(ThreadSafeContext *maybecx, size_t length, jschar **chars, size_t *capacity)
michael@0 140 {
michael@0 141 /*
michael@0 142 * String length doesn't include the null char, so include it here before
michael@0 143 * doubling. Adding the null char after doubling would interact poorly with
michael@0 144 * round-up malloc schemes.
michael@0 145 */
michael@0 146 size_t numChars = length + 1;
michael@0 147
michael@0 148 /*
michael@0 149 * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
michael@0 150 * next power of 2. This is similar to what we do with arrays; see
michael@0 151 * JSObject::ensureDenseArrayElements.
michael@0 152 */
michael@0 153 static const size_t DOUBLING_MAX = 1024 * 1024;
michael@0 154 numChars = numChars > DOUBLING_MAX ? numChars + (numChars / 8) : RoundUpPow2(numChars);
michael@0 155
michael@0 156 /* Like length, capacity does not include the null char, so take it out. */
michael@0 157 *capacity = numChars - 1;
michael@0 158
michael@0 159 JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
michael@0 160 size_t bytes = numChars * sizeof(jschar);
michael@0 161 *chars = (jschar *)(maybecx ? maybecx->malloc_(bytes) : js_malloc(bytes));
michael@0 162 return *chars != nullptr;
michael@0 163 }
michael@0 164
michael@0 165 bool
michael@0 166 JSRope::copyNonPureChars(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
michael@0 167 {
michael@0 168 return copyNonPureCharsInternal(cx, out, false);
michael@0 169 }
michael@0 170
michael@0 171 bool
michael@0 172 JSRope::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
michael@0 173 {
michael@0 174 return copyNonPureCharsInternal(cx, out, true);
michael@0 175 }
michael@0 176
michael@0 177 bool
michael@0 178 JSRope::copyNonPureCharsInternal(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out,
michael@0 179 bool nullTerminate) const
michael@0 180 {
michael@0 181 /*
michael@0 182 * Perform non-destructive post-order traversal of the rope, splatting
michael@0 183 * each node's characters into a contiguous buffer.
michael@0 184 */
michael@0 185
michael@0 186 size_t n = length();
michael@0 187 if (cx)
michael@0 188 out.reset(cx->pod_malloc<jschar>(n + 1));
michael@0 189 else
michael@0 190 out.reset(js_pod_malloc<jschar>(n + 1));
michael@0 191
michael@0 192 if (!out)
michael@0 193 return false;
michael@0 194
michael@0 195 Vector<const JSString *, 8, SystemAllocPolicy> nodeStack;
michael@0 196 const JSString *str = this;
michael@0 197 jschar *pos = out;
michael@0 198 while (true) {
michael@0 199 if (str->isRope()) {
michael@0 200 if (!nodeStack.append(str->asRope().rightChild()))
michael@0 201 return false;
michael@0 202 str = str->asRope().leftChild();
michael@0 203 } else {
michael@0 204 size_t len = str->length();
michael@0 205 PodCopy(pos, str->asLinear().chars(), len);
michael@0 206 pos += len;
michael@0 207 if (nodeStack.empty())
michael@0 208 break;
michael@0 209 str = nodeStack.popCopy();
michael@0 210 }
michael@0 211 }
michael@0 212
michael@0 213 JS_ASSERT(pos == out + n);
michael@0 214
michael@0 215 if (nullTerminate)
michael@0 216 out[n] = 0;
michael@0 217
michael@0 218 return true;
michael@0 219 }
michael@0 220
michael@0 221 template<JSRope::UsingBarrier b>
michael@0 222 JSFlatString *
michael@0 223 JSRope::flattenInternal(ExclusiveContext *maybecx)
michael@0 224 {
michael@0 225 /*
michael@0 226 * Perform a depth-first dag traversal, splatting each node's characters
michael@0 227 * into a contiguous buffer. Visit each rope node three times:
michael@0 228 * 1. record position in the buffer and recurse into left child;
michael@0 229 * 2. recurse into the right child;
michael@0 230 * 3. transform the node into a dependent string.
michael@0 231 * To avoid maintaining a stack, tree nodes are mutated to indicate how many
michael@0 232 * times they have been visited. Since ropes can be dags, a node may be
michael@0 233 * encountered multiple times during traversal. However, step 3 above leaves
michael@0 234 * a valid dependent string, so everything works out. This algorithm is
michael@0 235 * homomorphic to marking code.
michael@0 236 *
michael@0 237 * While ropes avoid all sorts of quadratic cases with string
michael@0 238 * concatenation, they can't help when ropes are immediately flattened.
michael@0 239 * One idiomatic case that we'd like to keep linear (and has traditionally
michael@0 240 * been linear in SM and other JS engines) is:
michael@0 241 *
michael@0 242 * while (...) {
michael@0 243 * s += ...
michael@0 244 * s.flatten
michael@0 245 * }
michael@0 246 *
michael@0 247 * To do this, when the buffer for a to-be-flattened rope is allocated, the
michael@0 248 * allocation size is rounded up. Then, if the resulting flat string is the
michael@0 249 * left-hand side of a new rope that gets flattened and there is enough
michael@0 250 * capacity, the rope is flattened into the same buffer, thereby avoiding
michael@0 251 * copying the left-hand side. Clearing the 'extensible' bit turns off this
michael@0 252 * optimization. This is necessary, e.g., when the JSAPI hands out the raw
michael@0 253 * null-terminated char array of a flat string.
michael@0 254 *
michael@0 255 * N.B. This optimization can create chains of dependent strings.
michael@0 256 */
michael@0 257 const size_t wholeLength = length();
michael@0 258 size_t wholeCapacity;
michael@0 259 jschar *wholeChars;
michael@0 260 JSString *str = this;
michael@0 261 jschar *pos;
michael@0 262
michael@0 263 /* Find the left most string, containing the first string. */
michael@0 264 JSRope *leftMostRope = this;
michael@0 265 while (leftMostRope->leftChild()->isRope())
michael@0 266 leftMostRope = &leftMostRope->leftChild()->asRope();
michael@0 267
michael@0 268 if (leftMostRope->leftChild()->isExtensible()) {
michael@0 269 JSExtensibleString &left = leftMostRope->leftChild()->asExtensible();
michael@0 270 size_t capacity = left.capacity();
michael@0 271 if (capacity >= wholeLength) {
michael@0 272 /*
michael@0 273 * Simulate a left-most traversal from the root to leftMost->leftChild()
michael@0 274 * via first_visit_node
michael@0 275 */
michael@0 276 while (str != leftMostRope) {
michael@0 277 JS_ASSERT(str->isRope());
michael@0 278 if (b == WithIncrementalBarrier) {
michael@0 279 JSString::writeBarrierPre(str->d.u1.left);
michael@0 280 JSString::writeBarrierPre(str->d.s.u2.right);
michael@0 281 }
michael@0 282 JSString *child = str->d.u1.left;
michael@0 283 str->d.u1.chars = left.chars();
michael@0 284 child->d.s.u3.parent = str;
michael@0 285 child->d.lengthAndFlags = 0x200;
michael@0 286 str = child;
michael@0 287 }
michael@0 288 if (b == WithIncrementalBarrier) {
michael@0 289 JSString::writeBarrierPre(str->d.u1.left);
michael@0 290 JSString::writeBarrierPre(str->d.s.u2.right);
michael@0 291 }
michael@0 292 str->d.u1.chars = left.chars();
michael@0 293 wholeCapacity = capacity;
michael@0 294 wholeChars = const_cast<jschar *>(left.chars());
michael@0 295 size_t bits = left.d.lengthAndFlags;
michael@0 296 pos = wholeChars + (bits >> LENGTH_SHIFT);
michael@0 297 JS_STATIC_ASSERT(!(EXTENSIBLE_FLAGS & DEPENDENT_FLAGS));
michael@0 298 left.d.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_FLAGS);
michael@0 299 left.d.s.u2.base = (JSLinearString *)this; /* will be true on exit */
michael@0 300 StringWriteBarrierPostRemove(maybecx, &left.d.u1.left);
michael@0 301 StringWriteBarrierPost(maybecx, (JSString **)&left.d.s.u2.base);
michael@0 302 goto visit_right_child;
michael@0 303 }
michael@0 304 }
michael@0 305
michael@0 306 if (!AllocChars(maybecx, wholeLength, &wholeChars, &wholeCapacity))
michael@0 307 return nullptr;
michael@0 308
michael@0 309 pos = wholeChars;
michael@0 310 first_visit_node: {
michael@0 311 if (b == WithIncrementalBarrier) {
michael@0 312 JSString::writeBarrierPre(str->d.u1.left);
michael@0 313 JSString::writeBarrierPre(str->d.s.u2.right);
michael@0 314 }
michael@0 315
michael@0 316 JSString &left = *str->d.u1.left;
michael@0 317 str->d.u1.chars = pos;
michael@0 318 StringWriteBarrierPostRemove(maybecx, &str->d.u1.left);
michael@0 319 if (left.isRope()) {
michael@0 320 left.d.s.u3.parent = str; /* Return to this when 'left' done, */
michael@0 321 left.d.lengthAndFlags = 0x200; /* but goto visit_right_child. */
michael@0 322 str = &left;
michael@0 323 goto first_visit_node;
michael@0 324 }
michael@0 325 size_t len = left.length();
michael@0 326 PodCopy(pos, left.d.u1.chars, len);
michael@0 327 pos += len;
michael@0 328 }
michael@0 329 visit_right_child: {
michael@0 330 JSString &right = *str->d.s.u2.right;
michael@0 331 if (right.isRope()) {
michael@0 332 right.d.s.u3.parent = str; /* Return to this node when 'right' done, */
michael@0 333 right.d.lengthAndFlags = 0x300; /* but goto finish_node. */
michael@0 334 str = &right;
michael@0 335 goto first_visit_node;
michael@0 336 }
michael@0 337 size_t len = right.length();
michael@0 338 PodCopy(pos, right.d.u1.chars, len);
michael@0 339 pos += len;
michael@0 340 }
michael@0 341 finish_node: {
michael@0 342 if (str == this) {
michael@0 343 JS_ASSERT(pos == wholeChars + wholeLength);
michael@0 344 *pos = '\0';
michael@0 345 str->d.lengthAndFlags = buildLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS);
michael@0 346 str->d.u1.chars = wholeChars;
michael@0 347 str->d.s.u2.capacity = wholeCapacity;
michael@0 348 StringWriteBarrierPostRemove(maybecx, &str->d.u1.left);
michael@0 349 StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.right);
michael@0 350 return &this->asFlat();
michael@0 351 }
michael@0 352 size_t progress = str->d.lengthAndFlags;
michael@0 353 str->d.lengthAndFlags = buildLengthAndFlags(pos - str->d.u1.chars, DEPENDENT_FLAGS);
michael@0 354 str->d.s.u2.base = (JSLinearString *)this; /* will be true on exit */
michael@0 355 StringWriteBarrierPost(maybecx, (JSString **)&str->d.s.u2.base);
michael@0 356 str = str->d.s.u3.parent;
michael@0 357 if (progress == 0x200)
michael@0 358 goto visit_right_child;
michael@0 359 JS_ASSERT(progress == 0x300);
michael@0 360 goto finish_node;
michael@0 361 }
michael@0 362 }
michael@0 363
michael@0 364 JSFlatString *
michael@0 365 JSRope::flatten(ExclusiveContext *maybecx)
michael@0 366 {
michael@0 367 #if JSGC_INCREMENTAL
michael@0 368 if (zone()->needsBarrier())
michael@0 369 return flattenInternal<WithIncrementalBarrier>(maybecx);
michael@0 370 else
michael@0 371 return flattenInternal<NoBarrier>(maybecx);
michael@0 372 #else
michael@0 373 return flattenInternal<NoBarrier>(maybecx);
michael@0 374 #endif
michael@0 375 }
michael@0 376
michael@0 377 template <AllowGC allowGC>
michael@0 378 JSString *
michael@0 379 js::ConcatStrings(ThreadSafeContext *cx,
michael@0 380 typename MaybeRooted<JSString*, allowGC>::HandleType left,
michael@0 381 typename MaybeRooted<JSString*, allowGC>::HandleType right)
michael@0 382 {
michael@0 383 JS_ASSERT_IF(!left->isAtom(), cx->isInsideCurrentZone(left));
michael@0 384 JS_ASSERT_IF(!right->isAtom(), cx->isInsideCurrentZone(right));
michael@0 385
michael@0 386 size_t leftLen = left->length();
michael@0 387 if (leftLen == 0)
michael@0 388 return right;
michael@0 389
michael@0 390 size_t rightLen = right->length();
michael@0 391 if (rightLen == 0)
michael@0 392 return left;
michael@0 393
michael@0 394 size_t wholeLength = leftLen + rightLen;
michael@0 395 if (!JSString::validateLength(cx, wholeLength))
michael@0 396 return nullptr;
michael@0 397
michael@0 398 if (JSFatInlineString::lengthFits(wholeLength) && cx->isJSContext()) {
michael@0 399 JSFatInlineString *str = js_NewGCFatInlineString<allowGC>(cx);
michael@0 400 if (!str)
michael@0 401 return nullptr;
michael@0 402
michael@0 403 ScopedThreadSafeStringInspector leftInspector(left);
michael@0 404 ScopedThreadSafeStringInspector rightInspector(right);
michael@0 405 if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
michael@0 406 return nullptr;
michael@0 407
michael@0 408 jschar *buf = str->init(wholeLength);
michael@0 409 PodCopy(buf, leftInspector.chars(), leftLen);
michael@0 410 PodCopy(buf + leftLen, rightInspector.chars(), rightLen);
michael@0 411
michael@0 412 buf[wholeLength] = 0;
michael@0 413 return str;
michael@0 414 }
michael@0 415
michael@0 416 return JSRope::new_<allowGC>(cx, left, right, wholeLength);
michael@0 417 }
michael@0 418
michael@0 419 template JSString *
michael@0 420 js::ConcatStrings<CanGC>(ThreadSafeContext *cx, HandleString left, HandleString right);
michael@0 421
michael@0 422 template JSString *
michael@0 423 js::ConcatStrings<NoGC>(ThreadSafeContext *cx, JSString *left, JSString *right);
michael@0 424
michael@0 425 bool
michael@0 426 JSDependentString::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const
michael@0 427 {
michael@0 428 JS_ASSERT(JSString::isDependent());
michael@0 429
michael@0 430 size_t n = length();
michael@0 431 jschar *s = cx->pod_malloc<jschar>(n + 1);
michael@0 432 if (!s)
michael@0 433 return false;
michael@0 434
michael@0 435 PodCopy(s, chars(), n);
michael@0 436 s[n] = 0;
michael@0 437
michael@0 438 out.reset(s);
michael@0 439 return true;
michael@0 440 }
michael@0 441
michael@0 442 JSFlatString *
michael@0 443 JSDependentString::undepend(ExclusiveContext *cx)
michael@0 444 {
michael@0 445 JS_ASSERT(JSString::isDependent());
michael@0 446
michael@0 447 /*
michael@0 448 * We destroy the base() pointer in undepend, so we need a pre-barrier. We
michael@0 449 * don't need a post-barrier because there aren't any outgoing pointers
michael@0 450 * afterwards.
michael@0 451 */
michael@0 452 JSString::writeBarrierPre(base());
michael@0 453
michael@0 454 size_t n = length();
michael@0 455 size_t size = (n + 1) * sizeof(jschar);
michael@0 456 jschar *s = (jschar *) cx->malloc_(size);
michael@0 457 if (!s)
michael@0 458 return nullptr;
michael@0 459
michael@0 460 PodCopy(s, chars(), n);
michael@0 461 s[n] = 0;
michael@0 462 d.u1.chars = s;
michael@0 463
michael@0 464 /*
michael@0 465 * Transform *this into an undepended string so 'base' will remain rooted
michael@0 466 * for the benefit of any other dependent string that depends on *this.
michael@0 467 */
michael@0 468 d.lengthAndFlags = buildLengthAndFlags(n, UNDEPENDED_FLAGS);
michael@0 469
michael@0 470 return &this->asFlat();
michael@0 471 }
michael@0 472
michael@0 473 bool
michael@0 474 JSFlatString::isIndexSlow(uint32_t *indexp) const
michael@0 475 {
michael@0 476 const jschar *s = charsZ();
michael@0 477 jschar ch = *s;
michael@0 478
michael@0 479 if (!JS7_ISDEC(ch))
michael@0 480 return false;
michael@0 481
michael@0 482 size_t n = length();
michael@0 483 if (n > UINT32_CHAR_BUFFER_LENGTH)
michael@0 484 return false;
michael@0 485
michael@0 486 /*
michael@0 487 * Make sure to account for the '\0' at the end of characters, dereferenced
michael@0 488 * in the loop below.
michael@0 489 */
michael@0 490 RangedPtr<const jschar> cp(s, n + 1);
michael@0 491 const RangedPtr<const jschar> end(s + n, s, n + 1);
michael@0 492
michael@0 493 uint32_t index = JS7_UNDEC(*cp++);
michael@0 494 uint32_t oldIndex = 0;
michael@0 495 uint32_t c = 0;
michael@0 496
michael@0 497 if (index != 0) {
michael@0 498 while (JS7_ISDEC(*cp)) {
michael@0 499 oldIndex = index;
michael@0 500 c = JS7_UNDEC(*cp);
michael@0 501 index = 10 * index + c;
michael@0 502 cp++;
michael@0 503 }
michael@0 504 }
michael@0 505
michael@0 506 /* It's not an element if there are characters after the number. */
michael@0 507 if (cp != end)
michael@0 508 return false;
michael@0 509
michael@0 510 /*
michael@0 511 * Look out for "4294967296" and larger-number strings that fit in
michael@0 512 * UINT32_CHAR_BUFFER_LENGTH: only unsigned 32-bit integers shall pass.
michael@0 513 */
michael@0 514 if (oldIndex < UINT32_MAX / 10 || (oldIndex == UINT32_MAX / 10 && c <= (UINT32_MAX % 10))) {
michael@0 515 *indexp = index;
michael@0 516 return true;
michael@0 517 }
michael@0 518
michael@0 519 return false;
michael@0 520 }
michael@0 521
michael@0 522 bool
michael@0 523 ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx)
michael@0 524 {
michael@0 525 if (chars_)
michael@0 526 return true;
michael@0 527
michael@0 528 if (cx->isExclusiveContext()) {
michael@0 529 JSLinearString *linear = str_->ensureLinear(cx->asExclusiveContext());
michael@0 530 if (!linear)
michael@0 531 return false;
michael@0 532 chars_ = linear->chars();
michael@0 533 } else {
michael@0 534 if (str_->hasPureChars()) {
michael@0 535 chars_ = str_->pureChars();
michael@0 536 } else {
michael@0 537 if (!str_->copyNonPureChars(cx, scopedChars_))
michael@0 538 return false;
michael@0 539 chars_ = scopedChars_;
michael@0 540 }
michael@0 541 }
michael@0 542
michael@0 543 JS_ASSERT(chars_);
michael@0 544 return true;
michael@0 545 }
michael@0 546
michael@0 547 /*
michael@0 548 * Set up some tools to make it easier to generate large tables. After constant
michael@0 549 * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
michael@0 550 * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
michael@0 551 * To use this, define R appropriately, then use Rn(0) (for some value of n), then
michael@0 552 * undefine R.
michael@0 553 */
michael@0 554 #define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0))
michael@0 555 #define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
michael@0 556 #define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
michael@0 557 #define R7(n) R6(n), R6((n) + (1 << 6))
michael@0 558
michael@0 559 /*
michael@0 560 * This is used when we generate our table of short strings, so the compiler is
michael@0 561 * happier if we use |c| as few times as possible.
michael@0 562 */
michael@0 563 #define FROM_SMALL_CHAR(c) jschar((c) + ((c) < 10 ? '0' : \
michael@0 564 (c) < 36 ? 'a' - 10 : \
michael@0 565 'A' - 36))
michael@0 566
michael@0 567 /*
michael@0 568 * Declare length-2 strings. We only store strings where both characters are
michael@0 569 * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
michael@0 570 * the lowercase letters, and the next 26 are the uppercase letters.
michael@0 571 */
michael@0 572 #define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
michael@0 573 (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \
michael@0 574 (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \
michael@0 575 StaticStrings::INVALID_SMALL_CHAR)
michael@0 576
michael@0 577 #define R TO_SMALL_CHAR
michael@0 578 const StaticStrings::SmallChar StaticStrings::toSmallChar[] = { R7(0) };
michael@0 579 #undef R
michael@0 580
michael@0 581 bool
michael@0 582 StaticStrings::init(JSContext *cx)
michael@0 583 {
michael@0 584 AutoLockForExclusiveAccess lock(cx);
michael@0 585 AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
michael@0 586
michael@0 587 for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
michael@0 588 jschar buffer[] = { jschar(i), '\0' };
michael@0 589 JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 1);
michael@0 590 if (!s)
michael@0 591 return false;
michael@0 592 unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
michael@0 593 }
michael@0 594
michael@0 595 for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
michael@0 596 jschar buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' };
michael@0 597 JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 2);
michael@0 598 if (!s)
michael@0 599 return false;
michael@0 600 length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
michael@0 601 }
michael@0 602
michael@0 603 for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
michael@0 604 if (i < 10) {
michael@0 605 intStaticTable[i] = unitStaticTable[i + '0'];
michael@0 606 } else if (i < 100) {
michael@0 607 size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) +
michael@0 608 TO_SMALL_CHAR((i % 10) + '0');
michael@0 609 intStaticTable[i] = length2StaticTable[index];
michael@0 610 } else {
michael@0 611 jschar buffer[] = { jschar('0' + (i / 100)),
michael@0 612 jschar('0' + ((i / 10) % 10)),
michael@0 613 jschar('0' + (i % 10)),
michael@0 614 '\0' };
michael@0 615 JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 3);
michael@0 616 if (!s)
michael@0 617 return false;
michael@0 618 intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
michael@0 619 }
michael@0 620 }
michael@0 621
michael@0 622 return true;
michael@0 623 }
michael@0 624
michael@0 625 void
michael@0 626 StaticStrings::trace(JSTracer *trc)
michael@0 627 {
michael@0 628 /* These strings never change, so barriers are not needed. */
michael@0 629
michael@0 630 for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++)
michael@0 631 MarkPermanentAtom(trc, unitStaticTable[i], "unit-static-string");
michael@0 632
michael@0 633 for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++)
michael@0 634 MarkPermanentAtom(trc, length2StaticTable[i], "length2-static-string");
michael@0 635
michael@0 636 /* This may mark some strings more than once, but so be it. */
michael@0 637 for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++)
michael@0 638 MarkPermanentAtom(trc, intStaticTable[i], "int-static-string");
michael@0 639 }
michael@0 640
michael@0 641 bool
michael@0 642 StaticStrings::isStatic(JSAtom *atom)
michael@0 643 {
michael@0 644 const jschar *chars = atom->chars();
michael@0 645 switch (atom->length()) {
michael@0 646 case 1:
michael@0 647 return chars[0] < UNIT_STATIC_LIMIT;
michael@0 648 case 2:
michael@0 649 return fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]);
michael@0 650 case 3:
michael@0 651 if ('1' <= chars[0] && chars[0] <= '9' &&
michael@0 652 '0' <= chars[1] && chars[1] <= '9' &&
michael@0 653 '0' <= chars[2] && chars[2] <= '9') {
michael@0 654 int i = (chars[0] - '0') * 100 +
michael@0 655 (chars[1] - '0') * 10 +
michael@0 656 (chars[2] - '0');
michael@0 657
michael@0 658 return unsigned(i) < INT_STATIC_LIMIT;
michael@0 659 }
michael@0 660 return false;
michael@0 661 default:
michael@0 662 return false;
michael@0 663 }
michael@0 664 }
michael@0 665
michael@0 666 #ifdef DEBUG
michael@0 667 void
michael@0 668 JSAtom::dump()
michael@0 669 {
michael@0 670 fprintf(stderr, "JSAtom* (%p) = ", (void *) this);
michael@0 671 this->JSString::dump();
michael@0 672 }
michael@0 673 #endif /* DEBUG */

mercurial