1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/String.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,673 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "vm/String-inl.h" 1.11 + 1.12 +#include "mozilla/MathAlgorithms.h" 1.13 +#include "mozilla/MemoryReporting.h" 1.14 +#include "mozilla/PodOperations.h" 1.15 +#include "mozilla/RangedPtr.h" 1.16 + 1.17 +#include "gc/Marking.h" 1.18 + 1.19 +#include "jscntxtinlines.h" 1.20 +#include "jscompartmentinlines.h" 1.21 + 1.22 +using namespace js; 1.23 + 1.24 +using mozilla::PodCopy; 1.25 +using mozilla::RangedPtr; 1.26 +using mozilla::RoundUpPow2; 1.27 + 1.28 +bool 1.29 +JSString::isFatInline() const 1.30 +{ 1.31 + // It's possible for fat-inline strings to be converted to flat strings; 1.32 + // as a result, checking just for the arena isn't enough to determine if a 1.33 + // string is fat-inline. Hence the isInline() check. 1.34 + bool is_FatInline = (getAllocKind() == gc::FINALIZE_FAT_INLINE_STRING) && isInline(); 1.35 + JS_ASSERT_IF(is_FatInline, isFlat()); 1.36 + return is_FatInline; 1.37 +} 1.38 + 1.39 +bool 1.40 +JSString::isExternal() const 1.41 +{ 1.42 + bool is_external = (getAllocKind() == gc::FINALIZE_EXTERNAL_STRING); 1.43 + JS_ASSERT_IF(is_external, isFlat()); 1.44 + return is_external; 1.45 +} 1.46 + 1.47 +size_t 1.48 +JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) 1.49 +{ 1.50 + // JSRope: do nothing, we'll count all children chars when we hit the leaf strings. 1.51 + if (isRope()) 1.52 + return 0; 1.53 + 1.54 + JS_ASSERT(isLinear()); 1.55 + 1.56 + // JSDependentString: do nothing, we'll count the chars when we hit the base string. 1.57 + if (isDependent()) 1.58 + return 0; 1.59 + 1.60 + JS_ASSERT(isFlat()); 1.61 + 1.62 + // JSExtensibleString: count the full capacity, not just the used space. 1.63 + if (isExtensible()) { 1.64 + JSExtensibleString &extensible = asExtensible(); 1.65 + return mallocSizeOf(extensible.chars()); 1.66 + } 1.67 + 1.68 + // JSExternalString: don't count, the chars could be stored anywhere. 1.69 + if (isExternal()) 1.70 + return 0; 1.71 + 1.72 + // JSInlineString, JSFatInlineString [JSInlineAtom, JSFatInlineAtom]: the chars are inline. 1.73 + if (isInline()) 1.74 + return 0; 1.75 + 1.76 + // JSAtom, JSUndependedString: measure the space for the chars. For 1.77 + // JSUndependedString, there is no need to count the base string, for the 1.78 + // same reason as JSDependentString above. 1.79 + JSFlatString &flat = asFlat(); 1.80 + return mallocSizeOf(flat.chars()); 1.81 +} 1.82 + 1.83 +#ifdef DEBUG 1.84 + 1.85 +void 1.86 +JSString::dumpChars(const jschar *s, size_t n) 1.87 +{ 1.88 + if (n == SIZE_MAX) { 1.89 + n = 0; 1.90 + while (s[n]) 1.91 + n++; 1.92 + } 1.93 + 1.94 + fputc('"', stderr); 1.95 + for (size_t i = 0; i < n; i++) { 1.96 + if (s[i] == '\n') 1.97 + fprintf(stderr, "\\n"); 1.98 + else if (s[i] == '\t') 1.99 + fprintf(stderr, "\\t"); 1.100 + else if (s[i] >= 32 && s[i] < 127) 1.101 + fputc(s[i], stderr); 1.102 + else if (s[i] <= 255) 1.103 + fprintf(stderr, "\\x%02x", (unsigned int) s[i]); 1.104 + else 1.105 + fprintf(stderr, "\\u%04x", (unsigned int) s[i]); 1.106 + } 1.107 + fputc('"', stderr); 1.108 +} 1.109 + 1.110 +void 1.111 +JSString::dump() 1.112 +{ 1.113 + if (const jschar *chars = getChars(nullptr)) { 1.114 + fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", 1.115 + (void *) this, (void *) chars); 1.116 + dumpChars(chars, length()); 1.117 + } else { 1.118 + fprintf(stderr, "(oom in JSString::dump)"); 1.119 + } 1.120 + fputc('\n', stderr); 1.121 +} 1.122 + 1.123 +bool 1.124 +JSString::equals(const char *s) 1.125 +{ 1.126 + const jschar *c = getChars(nullptr); 1.127 + if (!c) { 1.128 + fprintf(stderr, "OOM in JSString::equals!\n"); 1.129 + return false; 1.130 + } 1.131 + while (*c && *s) { 1.132 + if (*c != *s) 1.133 + return false; 1.134 + c++; 1.135 + s++; 1.136 + } 1.137 + return *c == *s; 1.138 +} 1.139 +#endif /* DEBUG */ 1.140 + 1.141 +static MOZ_ALWAYS_INLINE bool 1.142 +AllocChars(ThreadSafeContext *maybecx, size_t length, jschar **chars, size_t *capacity) 1.143 +{ 1.144 + /* 1.145 + * String length doesn't include the null char, so include it here before 1.146 + * doubling. Adding the null char after doubling would interact poorly with 1.147 + * round-up malloc schemes. 1.148 + */ 1.149 + size_t numChars = length + 1; 1.150 + 1.151 + /* 1.152 + * Grow by 12.5% if the buffer is very large. Otherwise, round up to the 1.153 + * next power of 2. This is similar to what we do with arrays; see 1.154 + * JSObject::ensureDenseArrayElements. 1.155 + */ 1.156 + static const size_t DOUBLING_MAX = 1024 * 1024; 1.157 + numChars = numChars > DOUBLING_MAX ? numChars + (numChars / 8) : RoundUpPow2(numChars); 1.158 + 1.159 + /* Like length, capacity does not include the null char, so take it out. */ 1.160 + *capacity = numChars - 1; 1.161 + 1.162 + JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX); 1.163 + size_t bytes = numChars * sizeof(jschar); 1.164 + *chars = (jschar *)(maybecx ? maybecx->malloc_(bytes) : js_malloc(bytes)); 1.165 + return *chars != nullptr; 1.166 +} 1.167 + 1.168 +bool 1.169 +JSRope::copyNonPureChars(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const 1.170 +{ 1.171 + return copyNonPureCharsInternal(cx, out, false); 1.172 +} 1.173 + 1.174 +bool 1.175 +JSRope::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const 1.176 +{ 1.177 + return copyNonPureCharsInternal(cx, out, true); 1.178 +} 1.179 + 1.180 +bool 1.181 +JSRope::copyNonPureCharsInternal(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out, 1.182 + bool nullTerminate) const 1.183 +{ 1.184 + /* 1.185 + * Perform non-destructive post-order traversal of the rope, splatting 1.186 + * each node's characters into a contiguous buffer. 1.187 + */ 1.188 + 1.189 + size_t n = length(); 1.190 + if (cx) 1.191 + out.reset(cx->pod_malloc<jschar>(n + 1)); 1.192 + else 1.193 + out.reset(js_pod_malloc<jschar>(n + 1)); 1.194 + 1.195 + if (!out) 1.196 + return false; 1.197 + 1.198 + Vector<const JSString *, 8, SystemAllocPolicy> nodeStack; 1.199 + const JSString *str = this; 1.200 + jschar *pos = out; 1.201 + while (true) { 1.202 + if (str->isRope()) { 1.203 + if (!nodeStack.append(str->asRope().rightChild())) 1.204 + return false; 1.205 + str = str->asRope().leftChild(); 1.206 + } else { 1.207 + size_t len = str->length(); 1.208 + PodCopy(pos, str->asLinear().chars(), len); 1.209 + pos += len; 1.210 + if (nodeStack.empty()) 1.211 + break; 1.212 + str = nodeStack.popCopy(); 1.213 + } 1.214 + } 1.215 + 1.216 + JS_ASSERT(pos == out + n); 1.217 + 1.218 + if (nullTerminate) 1.219 + out[n] = 0; 1.220 + 1.221 + return true; 1.222 +} 1.223 + 1.224 +template<JSRope::UsingBarrier b> 1.225 +JSFlatString * 1.226 +JSRope::flattenInternal(ExclusiveContext *maybecx) 1.227 +{ 1.228 + /* 1.229 + * Perform a depth-first dag traversal, splatting each node's characters 1.230 + * into a contiguous buffer. Visit each rope node three times: 1.231 + * 1. record position in the buffer and recurse into left child; 1.232 + * 2. recurse into the right child; 1.233 + * 3. transform the node into a dependent string. 1.234 + * To avoid maintaining a stack, tree nodes are mutated to indicate how many 1.235 + * times they have been visited. Since ropes can be dags, a node may be 1.236 + * encountered multiple times during traversal. However, step 3 above leaves 1.237 + * a valid dependent string, so everything works out. This algorithm is 1.238 + * homomorphic to marking code. 1.239 + * 1.240 + * While ropes avoid all sorts of quadratic cases with string 1.241 + * concatenation, they can't help when ropes are immediately flattened. 1.242 + * One idiomatic case that we'd like to keep linear (and has traditionally 1.243 + * been linear in SM and other JS engines) is: 1.244 + * 1.245 + * while (...) { 1.246 + * s += ... 1.247 + * s.flatten 1.248 + * } 1.249 + * 1.250 + * To do this, when the buffer for a to-be-flattened rope is allocated, the 1.251 + * allocation size is rounded up. Then, if the resulting flat string is the 1.252 + * left-hand side of a new rope that gets flattened and there is enough 1.253 + * capacity, the rope is flattened into the same buffer, thereby avoiding 1.254 + * copying the left-hand side. Clearing the 'extensible' bit turns off this 1.255 + * optimization. This is necessary, e.g., when the JSAPI hands out the raw 1.256 + * null-terminated char array of a flat string. 1.257 + * 1.258 + * N.B. This optimization can create chains of dependent strings. 1.259 + */ 1.260 + const size_t wholeLength = length(); 1.261 + size_t wholeCapacity; 1.262 + jschar *wholeChars; 1.263 + JSString *str = this; 1.264 + jschar *pos; 1.265 + 1.266 + /* Find the left most string, containing the first string. */ 1.267 + JSRope *leftMostRope = this; 1.268 + while (leftMostRope->leftChild()->isRope()) 1.269 + leftMostRope = &leftMostRope->leftChild()->asRope(); 1.270 + 1.271 + if (leftMostRope->leftChild()->isExtensible()) { 1.272 + JSExtensibleString &left = leftMostRope->leftChild()->asExtensible(); 1.273 + size_t capacity = left.capacity(); 1.274 + if (capacity >= wholeLength) { 1.275 + /* 1.276 + * Simulate a left-most traversal from the root to leftMost->leftChild() 1.277 + * via first_visit_node 1.278 + */ 1.279 + while (str != leftMostRope) { 1.280 + JS_ASSERT(str->isRope()); 1.281 + if (b == WithIncrementalBarrier) { 1.282 + JSString::writeBarrierPre(str->d.u1.left); 1.283 + JSString::writeBarrierPre(str->d.s.u2.right); 1.284 + } 1.285 + JSString *child = str->d.u1.left; 1.286 + str->d.u1.chars = left.chars(); 1.287 + child->d.s.u3.parent = str; 1.288 + child->d.lengthAndFlags = 0x200; 1.289 + str = child; 1.290 + } 1.291 + if (b == WithIncrementalBarrier) { 1.292 + JSString::writeBarrierPre(str->d.u1.left); 1.293 + JSString::writeBarrierPre(str->d.s.u2.right); 1.294 + } 1.295 + str->d.u1.chars = left.chars(); 1.296 + wholeCapacity = capacity; 1.297 + wholeChars = const_cast<jschar *>(left.chars()); 1.298 + size_t bits = left.d.lengthAndFlags; 1.299 + pos = wholeChars + (bits >> LENGTH_SHIFT); 1.300 + JS_STATIC_ASSERT(!(EXTENSIBLE_FLAGS & DEPENDENT_FLAGS)); 1.301 + left.d.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_FLAGS); 1.302 + left.d.s.u2.base = (JSLinearString *)this; /* will be true on exit */ 1.303 + StringWriteBarrierPostRemove(maybecx, &left.d.u1.left); 1.304 + StringWriteBarrierPost(maybecx, (JSString **)&left.d.s.u2.base); 1.305 + goto visit_right_child; 1.306 + } 1.307 + } 1.308 + 1.309 + if (!AllocChars(maybecx, wholeLength, &wholeChars, &wholeCapacity)) 1.310 + return nullptr; 1.311 + 1.312 + pos = wholeChars; 1.313 + first_visit_node: { 1.314 + if (b == WithIncrementalBarrier) { 1.315 + JSString::writeBarrierPre(str->d.u1.left); 1.316 + JSString::writeBarrierPre(str->d.s.u2.right); 1.317 + } 1.318 + 1.319 + JSString &left = *str->d.u1.left; 1.320 + str->d.u1.chars = pos; 1.321 + StringWriteBarrierPostRemove(maybecx, &str->d.u1.left); 1.322 + if (left.isRope()) { 1.323 + left.d.s.u3.parent = str; /* Return to this when 'left' done, */ 1.324 + left.d.lengthAndFlags = 0x200; /* but goto visit_right_child. */ 1.325 + str = &left; 1.326 + goto first_visit_node; 1.327 + } 1.328 + size_t len = left.length(); 1.329 + PodCopy(pos, left.d.u1.chars, len); 1.330 + pos += len; 1.331 + } 1.332 + visit_right_child: { 1.333 + JSString &right = *str->d.s.u2.right; 1.334 + if (right.isRope()) { 1.335 + right.d.s.u3.parent = str; /* Return to this node when 'right' done, */ 1.336 + right.d.lengthAndFlags = 0x300; /* but goto finish_node. */ 1.337 + str = &right; 1.338 + goto first_visit_node; 1.339 + } 1.340 + size_t len = right.length(); 1.341 + PodCopy(pos, right.d.u1.chars, len); 1.342 + pos += len; 1.343 + } 1.344 + finish_node: { 1.345 + if (str == this) { 1.346 + JS_ASSERT(pos == wholeChars + wholeLength); 1.347 + *pos = '\0'; 1.348 + str->d.lengthAndFlags = buildLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS); 1.349 + str->d.u1.chars = wholeChars; 1.350 + str->d.s.u2.capacity = wholeCapacity; 1.351 + StringWriteBarrierPostRemove(maybecx, &str->d.u1.left); 1.352 + StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.right); 1.353 + return &this->asFlat(); 1.354 + } 1.355 + size_t progress = str->d.lengthAndFlags; 1.356 + str->d.lengthAndFlags = buildLengthAndFlags(pos - str->d.u1.chars, DEPENDENT_FLAGS); 1.357 + str->d.s.u2.base = (JSLinearString *)this; /* will be true on exit */ 1.358 + StringWriteBarrierPost(maybecx, (JSString **)&str->d.s.u2.base); 1.359 + str = str->d.s.u3.parent; 1.360 + if (progress == 0x200) 1.361 + goto visit_right_child; 1.362 + JS_ASSERT(progress == 0x300); 1.363 + goto finish_node; 1.364 + } 1.365 +} 1.366 + 1.367 +JSFlatString * 1.368 +JSRope::flatten(ExclusiveContext *maybecx) 1.369 +{ 1.370 +#if JSGC_INCREMENTAL 1.371 + if (zone()->needsBarrier()) 1.372 + return flattenInternal<WithIncrementalBarrier>(maybecx); 1.373 + else 1.374 + return flattenInternal<NoBarrier>(maybecx); 1.375 +#else 1.376 + return flattenInternal<NoBarrier>(maybecx); 1.377 +#endif 1.378 +} 1.379 + 1.380 +template <AllowGC allowGC> 1.381 +JSString * 1.382 +js::ConcatStrings(ThreadSafeContext *cx, 1.383 + typename MaybeRooted<JSString*, allowGC>::HandleType left, 1.384 + typename MaybeRooted<JSString*, allowGC>::HandleType right) 1.385 +{ 1.386 + JS_ASSERT_IF(!left->isAtom(), cx->isInsideCurrentZone(left)); 1.387 + JS_ASSERT_IF(!right->isAtom(), cx->isInsideCurrentZone(right)); 1.388 + 1.389 + size_t leftLen = left->length(); 1.390 + if (leftLen == 0) 1.391 + return right; 1.392 + 1.393 + size_t rightLen = right->length(); 1.394 + if (rightLen == 0) 1.395 + return left; 1.396 + 1.397 + size_t wholeLength = leftLen + rightLen; 1.398 + if (!JSString::validateLength(cx, wholeLength)) 1.399 + return nullptr; 1.400 + 1.401 + if (JSFatInlineString::lengthFits(wholeLength) && cx->isJSContext()) { 1.402 + JSFatInlineString *str = js_NewGCFatInlineString<allowGC>(cx); 1.403 + if (!str) 1.404 + return nullptr; 1.405 + 1.406 + ScopedThreadSafeStringInspector leftInspector(left); 1.407 + ScopedThreadSafeStringInspector rightInspector(right); 1.408 + if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx)) 1.409 + return nullptr; 1.410 + 1.411 + jschar *buf = str->init(wholeLength); 1.412 + PodCopy(buf, leftInspector.chars(), leftLen); 1.413 + PodCopy(buf + leftLen, rightInspector.chars(), rightLen); 1.414 + 1.415 + buf[wholeLength] = 0; 1.416 + return str; 1.417 + } 1.418 + 1.419 + return JSRope::new_<allowGC>(cx, left, right, wholeLength); 1.420 +} 1.421 + 1.422 +template JSString * 1.423 +js::ConcatStrings<CanGC>(ThreadSafeContext *cx, HandleString left, HandleString right); 1.424 + 1.425 +template JSString * 1.426 +js::ConcatStrings<NoGC>(ThreadSafeContext *cx, JSString *left, JSString *right); 1.427 + 1.428 +bool 1.429 +JSDependentString::copyNonPureCharsZ(ThreadSafeContext *cx, ScopedJSFreePtr<jschar> &out) const 1.430 +{ 1.431 + JS_ASSERT(JSString::isDependent()); 1.432 + 1.433 + size_t n = length(); 1.434 + jschar *s = cx->pod_malloc<jschar>(n + 1); 1.435 + if (!s) 1.436 + return false; 1.437 + 1.438 + PodCopy(s, chars(), n); 1.439 + s[n] = 0; 1.440 + 1.441 + out.reset(s); 1.442 + return true; 1.443 +} 1.444 + 1.445 +JSFlatString * 1.446 +JSDependentString::undepend(ExclusiveContext *cx) 1.447 +{ 1.448 + JS_ASSERT(JSString::isDependent()); 1.449 + 1.450 + /* 1.451 + * We destroy the base() pointer in undepend, so we need a pre-barrier. We 1.452 + * don't need a post-barrier because there aren't any outgoing pointers 1.453 + * afterwards. 1.454 + */ 1.455 + JSString::writeBarrierPre(base()); 1.456 + 1.457 + size_t n = length(); 1.458 + size_t size = (n + 1) * sizeof(jschar); 1.459 + jschar *s = (jschar *) cx->malloc_(size); 1.460 + if (!s) 1.461 + return nullptr; 1.462 + 1.463 + PodCopy(s, chars(), n); 1.464 + s[n] = 0; 1.465 + d.u1.chars = s; 1.466 + 1.467 + /* 1.468 + * Transform *this into an undepended string so 'base' will remain rooted 1.469 + * for the benefit of any other dependent string that depends on *this. 1.470 + */ 1.471 + d.lengthAndFlags = buildLengthAndFlags(n, UNDEPENDED_FLAGS); 1.472 + 1.473 + return &this->asFlat(); 1.474 +} 1.475 + 1.476 +bool 1.477 +JSFlatString::isIndexSlow(uint32_t *indexp) const 1.478 +{ 1.479 + const jschar *s = charsZ(); 1.480 + jschar ch = *s; 1.481 + 1.482 + if (!JS7_ISDEC(ch)) 1.483 + return false; 1.484 + 1.485 + size_t n = length(); 1.486 + if (n > UINT32_CHAR_BUFFER_LENGTH) 1.487 + return false; 1.488 + 1.489 + /* 1.490 + * Make sure to account for the '\0' at the end of characters, dereferenced 1.491 + * in the loop below. 1.492 + */ 1.493 + RangedPtr<const jschar> cp(s, n + 1); 1.494 + const RangedPtr<const jschar> end(s + n, s, n + 1); 1.495 + 1.496 + uint32_t index = JS7_UNDEC(*cp++); 1.497 + uint32_t oldIndex = 0; 1.498 + uint32_t c = 0; 1.499 + 1.500 + if (index != 0) { 1.501 + while (JS7_ISDEC(*cp)) { 1.502 + oldIndex = index; 1.503 + c = JS7_UNDEC(*cp); 1.504 + index = 10 * index + c; 1.505 + cp++; 1.506 + } 1.507 + } 1.508 + 1.509 + /* It's not an element if there are characters after the number. */ 1.510 + if (cp != end) 1.511 + return false; 1.512 + 1.513 + /* 1.514 + * Look out for "4294967296" and larger-number strings that fit in 1.515 + * UINT32_CHAR_BUFFER_LENGTH: only unsigned 32-bit integers shall pass. 1.516 + */ 1.517 + if (oldIndex < UINT32_MAX / 10 || (oldIndex == UINT32_MAX / 10 && c <= (UINT32_MAX % 10))) { 1.518 + *indexp = index; 1.519 + return true; 1.520 + } 1.521 + 1.522 + return false; 1.523 +} 1.524 + 1.525 +bool 1.526 +ScopedThreadSafeStringInspector::ensureChars(ThreadSafeContext *cx) 1.527 +{ 1.528 + if (chars_) 1.529 + return true; 1.530 + 1.531 + if (cx->isExclusiveContext()) { 1.532 + JSLinearString *linear = str_->ensureLinear(cx->asExclusiveContext()); 1.533 + if (!linear) 1.534 + return false; 1.535 + chars_ = linear->chars(); 1.536 + } else { 1.537 + if (str_->hasPureChars()) { 1.538 + chars_ = str_->pureChars(); 1.539 + } else { 1.540 + if (!str_->copyNonPureChars(cx, scopedChars_)) 1.541 + return false; 1.542 + chars_ = scopedChars_; 1.543 + } 1.544 + } 1.545 + 1.546 + JS_ASSERT(chars_); 1.547 + return true; 1.548 +} 1.549 + 1.550 +/* 1.551 + * Set up some tools to make it easier to generate large tables. After constant 1.552 + * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1). 1.553 + * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1). 1.554 + * To use this, define R appropriately, then use Rn(0) (for some value of n), then 1.555 + * undefine R. 1.556 + */ 1.557 +#define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0)) 1.558 +#define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2)) 1.559 +#define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4)) 1.560 +#define R7(n) R6(n), R6((n) + (1 << 6)) 1.561 + 1.562 +/* 1.563 + * This is used when we generate our table of short strings, so the compiler is 1.564 + * happier if we use |c| as few times as possible. 1.565 + */ 1.566 +#define FROM_SMALL_CHAR(c) jschar((c) + ((c) < 10 ? '0' : \ 1.567 + (c) < 36 ? 'a' - 10 : \ 1.568 + 'A' - 36)) 1.569 + 1.570 +/* 1.571 + * Declare length-2 strings. We only store strings where both characters are 1.572 + * alphanumeric. The lower 10 short chars are the numerals, the next 26 are 1.573 + * the lowercase letters, and the next 26 are the uppercase letters. 1.574 + */ 1.575 +#define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \ 1.576 + (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \ 1.577 + (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \ 1.578 + StaticStrings::INVALID_SMALL_CHAR) 1.579 + 1.580 +#define R TO_SMALL_CHAR 1.581 +const StaticStrings::SmallChar StaticStrings::toSmallChar[] = { R7(0) }; 1.582 +#undef R 1.583 + 1.584 +bool 1.585 +StaticStrings::init(JSContext *cx) 1.586 +{ 1.587 + AutoLockForExclusiveAccess lock(cx); 1.588 + AutoCompartment ac(cx, cx->runtime()->atomsCompartment()); 1.589 + 1.590 + for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) { 1.591 + jschar buffer[] = { jschar(i), '\0' }; 1.592 + JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 1); 1.593 + if (!s) 1.594 + return false; 1.595 + unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(); 1.596 + } 1.597 + 1.598 + for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) { 1.599 + jschar buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' }; 1.600 + JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 2); 1.601 + if (!s) 1.602 + return false; 1.603 + length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(); 1.604 + } 1.605 + 1.606 + for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) { 1.607 + if (i < 10) { 1.608 + intStaticTable[i] = unitStaticTable[i + '0']; 1.609 + } else if (i < 100) { 1.610 + size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) + 1.611 + TO_SMALL_CHAR((i % 10) + '0'); 1.612 + intStaticTable[i] = length2StaticTable[index]; 1.613 + } else { 1.614 + jschar buffer[] = { jschar('0' + (i / 100)), 1.615 + jschar('0' + ((i / 10) % 10)), 1.616 + jschar('0' + (i % 10)), 1.617 + '\0' }; 1.618 + JSFlatString *s = js_NewStringCopyN<NoGC>(cx, buffer, 3); 1.619 + if (!s) 1.620 + return false; 1.621 + intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(); 1.622 + } 1.623 + } 1.624 + 1.625 + return true; 1.626 +} 1.627 + 1.628 +void 1.629 +StaticStrings::trace(JSTracer *trc) 1.630 +{ 1.631 + /* These strings never change, so barriers are not needed. */ 1.632 + 1.633 + for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) 1.634 + MarkPermanentAtom(trc, unitStaticTable[i], "unit-static-string"); 1.635 + 1.636 + for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) 1.637 + MarkPermanentAtom(trc, length2StaticTable[i], "length2-static-string"); 1.638 + 1.639 + /* This may mark some strings more than once, but so be it. */ 1.640 + for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) 1.641 + MarkPermanentAtom(trc, intStaticTable[i], "int-static-string"); 1.642 +} 1.643 + 1.644 +bool 1.645 +StaticStrings::isStatic(JSAtom *atom) 1.646 +{ 1.647 + const jschar *chars = atom->chars(); 1.648 + switch (atom->length()) { 1.649 + case 1: 1.650 + return chars[0] < UNIT_STATIC_LIMIT; 1.651 + case 2: 1.652 + return fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]); 1.653 + case 3: 1.654 + if ('1' <= chars[0] && chars[0] <= '9' && 1.655 + '0' <= chars[1] && chars[1] <= '9' && 1.656 + '0' <= chars[2] && chars[2] <= '9') { 1.657 + int i = (chars[0] - '0') * 100 + 1.658 + (chars[1] - '0') * 10 + 1.659 + (chars[2] - '0'); 1.660 + 1.661 + return unsigned(i) < INT_STATIC_LIMIT; 1.662 + } 1.663 + return false; 1.664 + default: 1.665 + return false; 1.666 + } 1.667 +} 1.668 + 1.669 +#ifdef DEBUG 1.670 +void 1.671 +JSAtom::dump() 1.672 +{ 1.673 + fprintf(stderr, "JSAtom* (%p) = ", (void *) this); 1.674 + this->JSString::dump(); 1.675 +} 1.676 +#endif /* DEBUG */