js/src/vm/String.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 #ifndef vm_String_h
michael@0 8 #define vm_String_h
michael@0 9
michael@0 10 #include "mozilla/MemoryReporting.h"
michael@0 11 #include "mozilla/PodOperations.h"
michael@0 12
michael@0 13 #include "jsapi.h"
michael@0 14 #include "jsfriendapi.h"
michael@0 15 #include "jsstr.h"
michael@0 16
michael@0 17 #include "gc/Barrier.h"
michael@0 18 #include "gc/Heap.h"
michael@0 19 #include "gc/Marking.h"
michael@0 20 #include "gc/Rooting.h"
michael@0 21 #include "js/CharacterEncoding.h"
michael@0 22 #include "js/RootingAPI.h"
michael@0 23
michael@0 24 class JSDependentString;
michael@0 25 class JSExtensibleString;
michael@0 26 class JSExternalString;
michael@0 27 class JSInlineString;
michael@0 28 class JSRope;
michael@0 29
michael@0 30 namespace js {
michael@0 31
michael@0 32 class StaticStrings;
michael@0 33 class PropertyName;
michael@0 34
michael@0 35 /* The buffer length required to contain any unsigned 32-bit integer. */
michael@0 36 static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
michael@0 37
michael@0 38 } /* namespace js */
michael@0 39
michael@0 40 /*
michael@0 41 * JavaScript strings
michael@0 42 *
michael@0 43 * Conceptually, a JS string is just an array of chars and a length. This array
michael@0 44 * of chars may or may not be null-terminated and, if it is, the null character
michael@0 45 * is not included in the length.
michael@0 46 *
michael@0 47 * To improve performance of common operations, the following optimizations are
michael@0 48 * made which affect the engine's representation of strings:
michael@0 49 *
michael@0 50 * - The plain vanilla representation is a "flat" string which consists of a
michael@0 51 * string header in the GC heap and a malloc'd null terminated char array.
michael@0 52 *
michael@0 53 * - To avoid copying a substring of an existing "base" string , a "dependent"
michael@0 54 * string (JSDependentString) can be created which points into the base
michael@0 55 * string's char array.
michael@0 56 *
michael@0 57 * - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created
michael@0 58 * to represent a delayed string concatenation. Concatenation (called
michael@0 59 * flattening) is performed if and when a linear char array is requested. In
michael@0 60 * general, ropes form a binary dag whose internal nodes are JSRope string
michael@0 61 * headers with no associated char array and whose leaf nodes are either flat
michael@0 62 * or dependent strings.
michael@0 63 *
michael@0 64 * - To avoid copying the left-hand side when flattening, the left-hand side's
michael@0 65 * buffer may be grown to make space for a copy of the right-hand side (see
michael@0 66 * comment in JSString::flatten). This optimization requires that there are
michael@0 67 * no external pointers into the char array. We conservatively maintain this
michael@0 68 * property via a flat string's "extensible" property.
michael@0 69 *
michael@0 70 * - To avoid allocating small char arrays, short strings can be stored inline
michael@0 71 * in the string header (JSInlineString). To increase the max size of such
michael@0 72 * inline strings, larger string headers can be used (JSFatInlineString).
michael@0 73 *
michael@0 74 * - To avoid comparing O(n) string equality comparison, strings can be
michael@0 75 * canonicalized to "atoms" (JSAtom) such that there is a single atom with a
michael@0 76 * given (length,chars).
michael@0 77 *
michael@0 78 * - To avoid copying all strings created through the JSAPI, an "external"
michael@0 79 * string (JSExternalString) can be created whose chars are managed by the
michael@0 80 * JSAPI client.
michael@0 81 *
michael@0 82 * Although all strings share the same basic memory layout, we can conceptually
michael@0 83 * arrange them into a hierarchy of operations/invariants and represent this
michael@0 84 * hierarchy in C++ with classes:
michael@0 85 *
michael@0 86 * C++ type operations+fields / invariants+properties
michael@0 87 * ========================== =========================================
michael@0 88 * JSString (abstract) getCharsZ, getChars, length / -
michael@0 89 * | \
michael@0 90 * | JSRope leftChild, rightChild / -
michael@0 91 * |
michael@0 92 * JSLinearString (abstract) chars / might be null-terminated
michael@0 93 * | \
michael@0 94 * | JSDependentString base / -
michael@0 95 * |
michael@0 96 * JSFlatString - / null terminated
michael@0 97 * | |
michael@0 98 * | +-- JSExternalString - / char array memory managed by embedding
michael@0 99 * | |
michael@0 100 * | +-- JSExtensibleString capacity / no external pointers into char array
michael@0 101 * | |
michael@0 102 * | +-- JSUndependedString original dependent base / -
michael@0 103 * | |
michael@0 104 * | +-- JSInlineString - / chars stored in header
michael@0 105 * | \
michael@0 106 * | JSFatInlineString - / header is fat
michael@0 107 * |
michael@0 108 * JSAtom - / string equality === pointer equality
michael@0 109 * |
michael@0 110 * js::PropertyName - / chars don't contain an index (uint32_t)
michael@0 111 *
michael@0 112 * Classes marked with (abstract) above are not literally C++ Abstract Base
michael@0 113 * Classes (since there are no virtual functions, pure or not, in this
michael@0 114 * hierarchy), but have the same meaning: there are no strings with this type as
michael@0 115 * its most-derived type.
michael@0 116 *
michael@0 117 * Atoms can additionally be permanent, i.e. unable to be collected, and can
michael@0 118 * be combined with other string types to create additional most-derived types
michael@0 119 * that satisfy the invariants of more than one of the abovementioned
michael@0 120 * most-derived types:
michael@0 121 * - InlineAtom = JSInlineString + JSAtom (atom with inline chars)
michael@0 122 * - FatInlineAtom = JSFatInlineString + JSAtom (atom with (more) inline chars)
michael@0 123 *
michael@0 124 * Derived string types can be queried from ancestor types via isX() and
michael@0 125 * retrieved with asX() debug-only-checked casts.
michael@0 126 *
michael@0 127 * The ensureX() operations mutate 'this' in place to effectively the type to be
michael@0 128 * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
michael@0 129 */
michael@0 130
michael@0 131 class JSString : public js::gc::BarrieredCell<JSString>
michael@0 132 {
michael@0 133 protected:
michael@0 134 static const size_t NUM_INLINE_CHARS = 2 * sizeof(void *) / sizeof(jschar);
michael@0 135
michael@0 136 /* Fields only apply to string types commented on the right. */
michael@0 137 struct Data
michael@0 138 {
michael@0 139 size_t lengthAndFlags; /* JSString */
michael@0 140 union {
michael@0 141 const jschar *chars; /* JSLinearString */
michael@0 142 JSString *left; /* JSRope */
michael@0 143 } u1;
michael@0 144 union {
michael@0 145 jschar inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|FatInline)String */
michael@0 146 struct {
michael@0 147 union {
michael@0 148 JSLinearString *base; /* JS(Dependent|Undepended)String */
michael@0 149 JSString *right; /* JSRope */
michael@0 150 size_t capacity; /* JSFlatString (extensible) */
michael@0 151 const JSStringFinalizer *externalFinalizer;/* JSExternalString */
michael@0 152 } u2;
michael@0 153 union {
michael@0 154 JSString *parent; /* JSRope (temporary) */
michael@0 155 size_t reserved; /* may use for bug 615290 */
michael@0 156 } u3;
michael@0 157 } s;
michael@0 158 };
michael@0 159 } d;
michael@0 160
michael@0 161 public:
michael@0 162 /* Flags exposed only for jits */
michael@0 163
michael@0 164 /*
michael@0 165 * The low LENGTH_SHIFT bits of lengthAndFlags are used to encode the type
michael@0 166 * of the string. The remaining bits store the string length (which must be
michael@0 167 * less or equal than MAX_LENGTH).
michael@0 168 *
michael@0 169 * Instead of using a dense index to represent the most-derived type, string
michael@0 170 * types are encoded to allow single-op tests for hot queries (isRope,
michael@0 171 * isDependent, isFlat, isAtom) which, in view of subtyping, would require
michael@0 172 * slower (isX() || isY() || isZ()).
michael@0 173 *
michael@0 174 * The string type encoding can be summarized as follows. The "instance
michael@0 175 * encoding" entry for a type specifies the flag bits used to create a
michael@0 176 * string instance of that type. Abstract types have no instances and thus
michael@0 177 * have no such entry. The "subtype predicate" entry for a type specifies
michael@0 178 * the predicate used to query whether a JSString instance is subtype
michael@0 179 * (reflexively) of that type.
michael@0 180 *
michael@0 181 * Rope 0000 0000
michael@0 182 * Linear - !0000
michael@0 183 * HasBase - xxx1
michael@0 184 * Dependent 0001 0001
michael@0 185 * Flat - isLinear && !isDependent
michael@0 186 * Undepended 0011 0011
michael@0 187 * Extensible 0010 0010
michael@0 188 * Inline 0100 isFlat && !isExtensible && (u1.chars == inlineStorage)
michael@0 189 * FatInline 0100 isInline && header in FINALIZE_FAT_INLINE_STRING arena
michael@0 190 * External 0100 header in FINALIZE_EXTERNAL_STRING arena
michael@0 191 * Atom - 1xxx
michael@0 192 * PermanentAtom 1100 1100
michael@0 193 * InlineAtom - isAtom && isInline
michael@0 194 * FatInlineAtom - isAtom && isFatInline
michael@0 195 *
michael@0 196 * "HasBase" here refers to the two string types that have a 'base' field:
michael@0 197 * JSDependentString and JSUndependedString.
michael@0 198 * A JSUndependedString is a JSDependentString which has been 'fixed' (by ensureFixed)
michael@0 199 * to be null-terminated. In such cases, the string must keep marking its base since
michael@0 200 * there may be any number of *other* JSDependentStrings transitively depending on it.
michael@0 201 *
michael@0 202 */
michael@0 203
michael@0 204 static const size_t LENGTH_SHIFT = 4;
michael@0 205 static const size_t FLAGS_MASK = JS_BITMASK(LENGTH_SHIFT);
michael@0 206
michael@0 207 static const size_t ROPE_FLAGS = 0;
michael@0 208 static const size_t DEPENDENT_FLAGS = JS_BIT(0);
michael@0 209 static const size_t UNDEPENDED_FLAGS = JS_BIT(0) | JS_BIT(1);
michael@0 210 static const size_t EXTENSIBLE_FLAGS = JS_BIT(1);
michael@0 211 static const size_t FIXED_FLAGS = JS_BIT(2);
michael@0 212
michael@0 213 static const size_t INT32_MASK = JS_BITMASK(3);
michael@0 214 static const size_t INT32_FLAGS = JS_BIT(1) | JS_BIT(2);
michael@0 215
michael@0 216 static const size_t HAS_BASE_BIT = JS_BIT(0);
michael@0 217 static const size_t PERMANENT_BIT = JS_BIT(2);
michael@0 218 static const size_t ATOM_BIT = JS_BIT(3);
michael@0 219
michael@0 220 static const size_t PERMANENT_ATOM_FLAGS = JS_BIT(2) | JS_BIT(3);
michael@0 221
michael@0 222 static const size_t MAX_LENGTH = JS_BIT(32 - LENGTH_SHIFT) - 1;
michael@0 223
michael@0 224 size_t buildLengthAndFlags(size_t length, size_t flags) {
michael@0 225 JS_ASSERT(length <= MAX_LENGTH);
michael@0 226 JS_ASSERT(flags <= FLAGS_MASK);
michael@0 227 return (length << LENGTH_SHIFT) | flags;
michael@0 228 }
michael@0 229
michael@0 230 /*
michael@0 231 * Helper function to validate that a string of a given length is
michael@0 232 * representable by a JSString. An allocation overflow is reported if false
michael@0 233 * is returned.
michael@0 234 */
michael@0 235 static inline bool validateLength(js::ThreadSafeContext *maybecx, size_t length);
michael@0 236
michael@0 237 static void staticAsserts() {
michael@0 238 JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
michael@0 239 JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >>
michael@0 240 JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH);
michael@0 241 JS_STATIC_ASSERT(sizeof(JSString) ==
michael@0 242 offsetof(JSString, d.inlineStorage) + NUM_INLINE_CHARS * sizeof(jschar));
michael@0 243 JS_STATIC_ASSERT(offsetof(JSString, d.u1.chars) ==
michael@0 244 offsetof(js::shadow::Atom, chars));
michael@0 245 }
michael@0 246
michael@0 247 /* Avoid lame compile errors in JSRope::flatten */
michael@0 248 friend class JSRope;
michael@0 249
michael@0 250 public:
michael@0 251 /* All strings have length. */
michael@0 252
michael@0 253 MOZ_ALWAYS_INLINE
michael@0 254 size_t length() const {
michael@0 255 return d.lengthAndFlags >> LENGTH_SHIFT;
michael@0 256 }
michael@0 257
michael@0 258 MOZ_ALWAYS_INLINE
michael@0 259 bool empty() const {
michael@0 260 return d.lengthAndFlags <= FLAGS_MASK;
michael@0 261 }
michael@0 262
michael@0 263 /*
michael@0 264 * All strings have a fallible operation to get an array of chars.
michael@0 265 * getCharsZ additionally ensures the array is null terminated.
michael@0 266 */
michael@0 267
michael@0 268 inline const jschar *getChars(js::ExclusiveContext *cx);
michael@0 269 inline const jschar *getCharsZ(js::ExclusiveContext *cx);
michael@0 270 inline bool getChar(js::ExclusiveContext *cx, size_t index, jschar *code);
michael@0 271
michael@0 272 /*
michael@0 273 * A string has "pure" chars if it can return a pointer to its chars
michael@0 274 * infallibly without mutating anything so they are safe to be from off the
michael@0 275 * main thread. If a string does not have pure chars, the caller can call
michael@0 276 * copyNonPureChars to allocate a copy of the chars which is also a
michael@0 277 * non-mutating threadsafe operation. Beware, this is an O(n) operation
michael@0 278 * (involving a DAG traversal for ropes).
michael@0 279 */
michael@0 280 bool hasPureChars() const { return isLinear(); }
michael@0 281 bool hasPureCharsZ() const { return isFlat(); }
michael@0 282 inline const jschar *pureChars() const;
michael@0 283 inline const jschar *pureCharsZ() const;
michael@0 284 inline bool copyNonPureChars(js::ThreadSafeContext *cx,
michael@0 285 js::ScopedJSFreePtr<jschar> &out) const;
michael@0 286 inline bool copyNonPureCharsZ(js::ThreadSafeContext *cx,
michael@0 287 js::ScopedJSFreePtr<jschar> &out) const;
michael@0 288
michael@0 289 /* Fallible conversions to more-derived string types. */
michael@0 290
michael@0 291 inline JSLinearString *ensureLinear(js::ExclusiveContext *cx);
michael@0 292 inline JSFlatString *ensureFlat(js::ExclusiveContext *cx);
michael@0 293
michael@0 294 static bool ensureLinear(js::ExclusiveContext *cx, JSString *str) {
michael@0 295 return str->ensureLinear(cx) != nullptr;
michael@0 296 }
michael@0 297
michael@0 298 /* Type query and debug-checked casts */
michael@0 299
michael@0 300 MOZ_ALWAYS_INLINE
michael@0 301 bool isRope() const {
michael@0 302 return (d.lengthAndFlags & FLAGS_MASK) == ROPE_FLAGS;
michael@0 303 }
michael@0 304
michael@0 305 MOZ_ALWAYS_INLINE
michael@0 306 JSRope &asRope() const {
michael@0 307 JS_ASSERT(isRope());
michael@0 308 return *(JSRope *)this;
michael@0 309 }
michael@0 310
michael@0 311 MOZ_ALWAYS_INLINE
michael@0 312 bool isLinear() const {
michael@0 313 return !isRope();
michael@0 314 }
michael@0 315
michael@0 316 MOZ_ALWAYS_INLINE
michael@0 317 JSLinearString &asLinear() const {
michael@0 318 JS_ASSERT(JSString::isLinear());
michael@0 319 return *(JSLinearString *)this;
michael@0 320 }
michael@0 321
michael@0 322 MOZ_ALWAYS_INLINE
michael@0 323 bool isDependent() const {
michael@0 324 return (d.lengthAndFlags & FLAGS_MASK) == DEPENDENT_FLAGS;
michael@0 325 }
michael@0 326
michael@0 327 MOZ_ALWAYS_INLINE
michael@0 328 JSDependentString &asDependent() const {
michael@0 329 JS_ASSERT(isDependent());
michael@0 330 return *(JSDependentString *)this;
michael@0 331 }
michael@0 332
michael@0 333 MOZ_ALWAYS_INLINE
michael@0 334 bool isFlat() const {
michael@0 335 return isLinear() && !isDependent();
michael@0 336 }
michael@0 337
michael@0 338 MOZ_ALWAYS_INLINE
michael@0 339 JSFlatString &asFlat() const {
michael@0 340 JS_ASSERT(isFlat());
michael@0 341 return *(JSFlatString *)this;
michael@0 342 }
michael@0 343
michael@0 344 MOZ_ALWAYS_INLINE
michael@0 345 bool isExtensible() const {
michael@0 346 return (d.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS;
michael@0 347 }
michael@0 348
michael@0 349 MOZ_ALWAYS_INLINE
michael@0 350 JSExtensibleString &asExtensible() const {
michael@0 351 JS_ASSERT(isExtensible());
michael@0 352 return *(JSExtensibleString *)this;
michael@0 353 }
michael@0 354
michael@0 355 MOZ_ALWAYS_INLINE
michael@0 356 bool isInline() const {
michael@0 357 return isFlat() && !isExtensible() && (d.u1.chars == d.inlineStorage);
michael@0 358 }
michael@0 359
michael@0 360 MOZ_ALWAYS_INLINE
michael@0 361 JSInlineString &asInline() const {
michael@0 362 JS_ASSERT(isInline());
michael@0 363 return *(JSInlineString *)this;
michael@0 364 }
michael@0 365
michael@0 366 bool isFatInline() const;
michael@0 367
michael@0 368 /* For hot code, prefer other type queries. */
michael@0 369 bool isExternal() const;
michael@0 370
michael@0 371 MOZ_ALWAYS_INLINE
michael@0 372 JSExternalString &asExternal() const {
michael@0 373 JS_ASSERT(isExternal());
michael@0 374 return *(JSExternalString *)this;
michael@0 375 }
michael@0 376
michael@0 377 MOZ_ALWAYS_INLINE
michael@0 378 bool isUndepended() const {
michael@0 379 return (d.lengthAndFlags & FLAGS_MASK) == UNDEPENDED_FLAGS;
michael@0 380 }
michael@0 381
michael@0 382 MOZ_ALWAYS_INLINE
michael@0 383 bool isAtom() const {
michael@0 384 return d.lengthAndFlags & ATOM_BIT;
michael@0 385 }
michael@0 386
michael@0 387 MOZ_ALWAYS_INLINE
michael@0 388 bool isPermanentAtom() const {
michael@0 389 return (d.lengthAndFlags & FLAGS_MASK) == PERMANENT_ATOM_FLAGS;
michael@0 390 }
michael@0 391
michael@0 392 MOZ_ALWAYS_INLINE
michael@0 393 JSAtom &asAtom() const {
michael@0 394 JS_ASSERT(isAtom());
michael@0 395 return *(JSAtom *)this;
michael@0 396 }
michael@0 397
michael@0 398 /* Only called by the GC for dependent or undepended strings. */
michael@0 399
michael@0 400 inline bool hasBase() const {
michael@0 401 JS_STATIC_ASSERT((DEPENDENT_FLAGS | JS_BIT(1)) == UNDEPENDED_FLAGS);
michael@0 402 return d.lengthAndFlags & HAS_BASE_BIT;
michael@0 403 }
michael@0 404
michael@0 405 inline JSLinearString *base() const;
michael@0 406
michael@0 407 inline void markBase(JSTracer *trc);
michael@0 408
michael@0 409 /* Only called by the GC for strings with the FINALIZE_STRING kind. */
michael@0 410
michael@0 411 inline void finalize(js::FreeOp *fop);
michael@0 412
michael@0 413 /* Gets the number of bytes that the chars take on the heap. */
michael@0 414
michael@0 415 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
michael@0 416
michael@0 417 /* Offsets for direct field from jit code. */
michael@0 418
michael@0 419 static size_t offsetOfLengthAndFlags() {
michael@0 420 return offsetof(JSString, d.lengthAndFlags);
michael@0 421 }
michael@0 422
michael@0 423 static size_t offsetOfChars() {
michael@0 424 return offsetof(JSString, d.u1.chars);
michael@0 425 }
michael@0 426
michael@0 427 js::gc::AllocKind getAllocKind() const { return tenuredGetAllocKind(); }
michael@0 428
michael@0 429 static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; }
michael@0 430
michael@0 431 #ifdef DEBUG
michael@0 432 void dump();
michael@0 433 static void dumpChars(const jschar *s, size_t len);
michael@0 434 bool equals(const char *s);
michael@0 435 #endif
michael@0 436
michael@0 437 static MOZ_ALWAYS_INLINE void readBarrier(JSString *thing) {
michael@0 438 #ifdef JSGC_INCREMENTAL
michael@0 439 if (thing->isPermanentAtom())
michael@0 440 return;
michael@0 441
michael@0 442 js::gc::BarrieredCell<JSString>::readBarrier(thing);
michael@0 443 #endif
michael@0 444 }
michael@0 445
michael@0 446 static MOZ_ALWAYS_INLINE void writeBarrierPre(JSString *thing) {
michael@0 447 #ifdef JSGC_INCREMENTAL
michael@0 448 if (isNullLike(thing) || thing->isPermanentAtom())
michael@0 449 return;
michael@0 450
michael@0 451 js::gc::BarrieredCell<JSString>::writeBarrierPre(thing);
michael@0 452 #endif
michael@0 453 }
michael@0 454
michael@0 455 private:
michael@0 456 JSString() MOZ_DELETE;
michael@0 457 JSString(const JSString &other) MOZ_DELETE;
michael@0 458 void operator=(const JSString &other) MOZ_DELETE;
michael@0 459 };
michael@0 460
michael@0 461 class JSRope : public JSString
michael@0 462 {
michael@0 463 bool copyNonPureCharsInternal(js::ThreadSafeContext *cx,
michael@0 464 js::ScopedJSFreePtr<jschar> &out,
michael@0 465 bool nullTerminate) const;
michael@0 466 bool copyNonPureChars(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
michael@0 467 bool copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
michael@0 468
michael@0 469 enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
michael@0 470 template<UsingBarrier b>
michael@0 471 JSFlatString *flattenInternal(js::ExclusiveContext *cx);
michael@0 472
michael@0 473 friend class JSString;
michael@0 474 JSFlatString *flatten(js::ExclusiveContext *cx);
michael@0 475
michael@0 476 void init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t length);
michael@0 477
michael@0 478 public:
michael@0 479 template <js::AllowGC allowGC>
michael@0 480 static inline JSRope *new_(js::ThreadSafeContext *cx,
michael@0 481 typename js::MaybeRooted<JSString*, allowGC>::HandleType left,
michael@0 482 typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
michael@0 483 size_t length);
michael@0 484
michael@0 485 inline JSString *leftChild() const {
michael@0 486 JS_ASSERT(isRope());
michael@0 487 return d.u1.left;
michael@0 488 }
michael@0 489
michael@0 490 inline JSString *rightChild() const {
michael@0 491 JS_ASSERT(isRope());
michael@0 492 return d.s.u2.right;
michael@0 493 }
michael@0 494
michael@0 495 inline void markChildren(JSTracer *trc);
michael@0 496
michael@0 497 inline static size_t offsetOfLeft() {
michael@0 498 return offsetof(JSRope, d.u1.left);
michael@0 499 }
michael@0 500 inline static size_t offsetOfRight() {
michael@0 501 return offsetof(JSRope, d.s.u2.right);
michael@0 502 }
michael@0 503 };
michael@0 504
michael@0 505 JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
michael@0 506
michael@0 507 class JSLinearString : public JSString
michael@0 508 {
michael@0 509 friend class JSString;
michael@0 510
michael@0 511 /* Vacuous and therefore unimplemented. */
michael@0 512 JSLinearString *ensureLinear(JSContext *cx) MOZ_DELETE;
michael@0 513 bool isLinear() const MOZ_DELETE;
michael@0 514 JSLinearString &asLinear() const MOZ_DELETE;
michael@0 515
michael@0 516 public:
michael@0 517 MOZ_ALWAYS_INLINE
michael@0 518 const jschar *chars() const {
michael@0 519 JS_ASSERT(JSString::isLinear());
michael@0 520 return d.u1.chars;
michael@0 521 }
michael@0 522
michael@0 523 JS::TwoByteChars range() const {
michael@0 524 JS_ASSERT(JSString::isLinear());
michael@0 525 return JS::TwoByteChars(d.u1.chars, length());
michael@0 526 }
michael@0 527 };
michael@0 528
michael@0 529 JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString));
michael@0 530
michael@0 531 class JSDependentString : public JSLinearString
michael@0 532 {
michael@0 533 bool copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
michael@0 534
michael@0 535 friend class JSString;
michael@0 536 JSFlatString *undepend(js::ExclusiveContext *cx);
michael@0 537
michael@0 538 void init(js::ThreadSafeContext *cx, JSLinearString *base, const jschar *chars,
michael@0 539 size_t length);
michael@0 540
michael@0 541 /* Vacuous and therefore unimplemented. */
michael@0 542 bool isDependent() const MOZ_DELETE;
michael@0 543 JSDependentString &asDependent() const MOZ_DELETE;
michael@0 544
michael@0 545 public:
michael@0 546 static inline JSLinearString *new_(js::ExclusiveContext *cx, JSLinearString *base,
michael@0 547 const jschar *chars, size_t length);
michael@0 548 };
michael@0 549
michael@0 550 JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
michael@0 551
michael@0 552 class JSFlatString : public JSLinearString
michael@0 553 {
michael@0 554 /* Vacuous and therefore unimplemented. */
michael@0 555 JSFlatString *ensureFlat(JSContext *cx) MOZ_DELETE;
michael@0 556 bool isFlat() const MOZ_DELETE;
michael@0 557 JSFlatString &asFlat() const MOZ_DELETE;
michael@0 558
michael@0 559 bool isIndexSlow(uint32_t *indexp) const;
michael@0 560
michael@0 561 void init(const jschar *chars, size_t length);
michael@0 562
michael@0 563 public:
michael@0 564 template <js::AllowGC allowGC>
michael@0 565 static inline JSFlatString *new_(js::ThreadSafeContext *cx,
michael@0 566 const jschar *chars, size_t length);
michael@0 567
michael@0 568 MOZ_ALWAYS_INLINE
michael@0 569 const jschar *charsZ() const {
michael@0 570 JS_ASSERT(JSString::isFlat());
michael@0 571 return chars();
michael@0 572 }
michael@0 573
michael@0 574 /*
michael@0 575 * Returns true if this string's characters store an unsigned 32-bit
michael@0 576 * integer value, initializing *indexp to that value if so. (Thus if
michael@0 577 * calling isIndex returns true, js::IndexToString(cx, *indexp) will be a
michael@0 578 * string equal to this string.)
michael@0 579 */
michael@0 580 inline bool isIndex(uint32_t *indexp) const {
michael@0 581 const jschar *s = chars();
michael@0 582 return JS7_ISDEC(*s) && isIndexSlow(indexp);
michael@0 583 }
michael@0 584
michael@0 585 /*
michael@0 586 * Returns a property name represented by this string, or null on failure.
michael@0 587 * You must verify that this is not an index per isIndex before calling
michael@0 588 * this method.
michael@0 589 */
michael@0 590 inline js::PropertyName *toPropertyName(JSContext *cx);
michael@0 591
michael@0 592 /*
michael@0 593 * Once a JSFlatString sub-class has been added to the atom state, this
michael@0 594 * operation changes the string to the JSAtom type, in place.
michael@0 595 */
michael@0 596 MOZ_ALWAYS_INLINE JSAtom *morphAtomizedStringIntoAtom() {
michael@0 597 d.lengthAndFlags = buildLengthAndFlags(length(), ATOM_BIT);
michael@0 598 return &asAtom();
michael@0 599 }
michael@0 600 MOZ_ALWAYS_INLINE JSAtom *morphAtomizedStringIntoPermanentAtom() {
michael@0 601 d.lengthAndFlags = buildLengthAndFlags(length(), PERMANENT_ATOM_FLAGS);
michael@0 602 return &asAtom();
michael@0 603 }
michael@0 604
michael@0 605 inline void finalize(js::FreeOp *fop);
michael@0 606 };
michael@0 607
michael@0 608 JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
michael@0 609
michael@0 610 class JSExtensibleString : public JSFlatString
michael@0 611 {
michael@0 612 /* Vacuous and therefore unimplemented. */
michael@0 613 bool isExtensible() const MOZ_DELETE;
michael@0 614 JSExtensibleString &asExtensible() const MOZ_DELETE;
michael@0 615
michael@0 616 public:
michael@0 617 MOZ_ALWAYS_INLINE
michael@0 618 size_t capacity() const {
michael@0 619 JS_ASSERT(JSString::isExtensible());
michael@0 620 return d.s.u2.capacity;
michael@0 621 }
michael@0 622 };
michael@0 623
michael@0 624 JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
michael@0 625
michael@0 626 /* On 32-bit platforms, MAX_INLINE_LENGTH is 4. On 64-bit platforms it is 8. */
michael@0 627 class JSInlineString : public JSFlatString
michael@0 628 {
michael@0 629 static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
michael@0 630
michael@0 631 public:
michael@0 632 template <js::AllowGC allowGC>
michael@0 633 static inline JSInlineString *new_(js::ThreadSafeContext *cx);
michael@0 634
michael@0 635 inline jschar *init(size_t length);
michael@0 636
michael@0 637 inline void resetLength(size_t length);
michael@0 638
michael@0 639 static bool lengthFits(size_t length) {
michael@0 640 return length <= MAX_INLINE_LENGTH;
michael@0 641 }
michael@0 642
michael@0 643 static size_t offsetOfInlineStorage() {
michael@0 644 return offsetof(JSInlineString, d.inlineStorage);
michael@0 645 }
michael@0 646 };
michael@0 647
michael@0 648 JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
michael@0 649
michael@0 650 /*
michael@0 651 * On both 32-bit and 64-bit platforms, INLINE_EXTENSION_CHARS is 12. This is
michael@0 652 * deliberate, in order to minimize potential performance differences between
michael@0 653 * 32-bit and 64-bit platforms.
michael@0 654 *
michael@0 655 * There are still some differences due to NUM_INLINE_CHARS being different.
michael@0 656 * E.g. strings of length 4--7 will be JSFatInlineStrings on 32-bit platforms
michael@0 657 * and JSInlineStrings on 64-bit platforms. But the more significant transition
michael@0 658 * from inline strings to non-inline strings occurs at length 12 on both 32-bit
michael@0 659 * and 64-bit platforms.
michael@0 660 */
michael@0 661 class JSFatInlineString : public JSInlineString
michael@0 662 {
michael@0 663 static const size_t INLINE_EXTENSION_CHARS = 12 - NUM_INLINE_CHARS;
michael@0 664
michael@0 665 static void staticAsserts() {
michael@0 666 JS_STATIC_ASSERT((INLINE_EXTENSION_CHARS * sizeof(jschar)) % js::gc::CellSize == 0);
michael@0 667 JS_STATIC_ASSERT(MAX_FAT_INLINE_LENGTH + 1 ==
michael@0 668 (sizeof(JSFatInlineString) -
michael@0 669 offsetof(JSFatInlineString, d.inlineStorage)) / sizeof(jschar));
michael@0 670 }
michael@0 671
michael@0 672 protected: /* to fool clang into not warning this is unused */
michael@0 673 jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
michael@0 674
michael@0 675 public:
michael@0 676 template <js::AllowGC allowGC>
michael@0 677 static inline JSFatInlineString *new_(js::ThreadSafeContext *cx);
michael@0 678
michael@0 679 static const size_t MAX_FAT_INLINE_LENGTH = JSString::NUM_INLINE_CHARS +
michael@0 680 INLINE_EXTENSION_CHARS
michael@0 681 -1 /* null terminator */;
michael@0 682
michael@0 683 static bool lengthFits(size_t length) {
michael@0 684 return length <= MAX_FAT_INLINE_LENGTH;
michael@0 685 }
michael@0 686
michael@0 687 /* Only called by the GC for strings with the FINALIZE_FAT_INLINE_STRING kind. */
michael@0 688
michael@0 689 MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop);
michael@0 690 };
michael@0 691
michael@0 692 JS_STATIC_ASSERT(sizeof(JSFatInlineString) % js::gc::CellSize == 0);
michael@0 693
michael@0 694 class JSExternalString : public JSFlatString
michael@0 695 {
michael@0 696 void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
michael@0 697
michael@0 698 /* Vacuous and therefore unimplemented. */
michael@0 699 bool isExternal() const MOZ_DELETE;
michael@0 700 JSExternalString &asExternal() const MOZ_DELETE;
michael@0 701
michael@0 702 public:
michael@0 703 static inline JSExternalString *new_(JSContext *cx, const jschar *chars, size_t length,
michael@0 704 const JSStringFinalizer *fin);
michael@0 705
michael@0 706 const JSStringFinalizer *externalFinalizer() const {
michael@0 707 JS_ASSERT(JSString::isExternal());
michael@0 708 return d.s.u2.externalFinalizer;
michael@0 709 }
michael@0 710
michael@0 711 /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
michael@0 712
michael@0 713 inline void finalize(js::FreeOp *fop);
michael@0 714 };
michael@0 715
michael@0 716 JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
michael@0 717
michael@0 718 class JSUndependedString : public JSFlatString
michael@0 719 {
michael@0 720 /*
michael@0 721 * JSUndependedString is not explicitly used and is only present for
michael@0 722 * consistency. See JSDependentString::undepend for how a JSDependentString
michael@0 723 * gets morphed into a JSUndependedString.
michael@0 724 */
michael@0 725 };
michael@0 726
michael@0 727 JS_STATIC_ASSERT(sizeof(JSUndependedString) == sizeof(JSString));
michael@0 728
michael@0 729 class JSAtom : public JSFlatString
michael@0 730 {
michael@0 731 /* Vacuous and therefore unimplemented. */
michael@0 732 bool isAtom() const MOZ_DELETE;
michael@0 733 JSAtom &asAtom() const MOZ_DELETE;
michael@0 734
michael@0 735 public:
michael@0 736 /* Returns the PropertyName for this. isIndex() must be false. */
michael@0 737 inline js::PropertyName *asPropertyName();
michael@0 738
michael@0 739 inline void finalize(js::FreeOp *fop);
michael@0 740
michael@0 741 MOZ_ALWAYS_INLINE
michael@0 742 bool isPermanent() const {
michael@0 743 return d.lengthAndFlags & PERMANENT_BIT;
michael@0 744 }
michael@0 745
michael@0 746 // Transform this atom into a permanent atom. This is only done during
michael@0 747 // initialization of the runtime.
michael@0 748 MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
michael@0 749 d.lengthAndFlags = buildLengthAndFlags(length(), PERMANENT_ATOM_FLAGS);
michael@0 750 }
michael@0 751
michael@0 752 #ifdef DEBUG
michael@0 753 void dump();
michael@0 754 #endif
michael@0 755 };
michael@0 756
michael@0 757 JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
michael@0 758
michael@0 759 namespace js {
michael@0 760
michael@0 761 /*
michael@0 762 * Thread safe RAII wrapper for inspecting the contents of JSStrings. The
michael@0 763 * thread safe operations such as |getCharsNonDestructive| require allocation
michael@0 764 * of a char array. This allocation is not always required, such as when the
michael@0 765 * string is already linear. This wrapper makes dealing with this detail more
michael@0 766 * convenient by encapsulating the allocation logic.
michael@0 767 *
michael@0 768 * As the name suggests, this class is scoped. Return values from chars() and
michael@0 769 * range() may not be valid after the inspector goes out of scope.
michael@0 770 */
michael@0 771
michael@0 772 class ScopedThreadSafeStringInspector
michael@0 773 {
michael@0 774 private:
michael@0 775 JSString *str_;
michael@0 776 ScopedJSFreePtr<jschar> scopedChars_;
michael@0 777 const jschar *chars_;
michael@0 778
michael@0 779 public:
michael@0 780 ScopedThreadSafeStringInspector(JSString *str)
michael@0 781 : str_(str),
michael@0 782 chars_(nullptr)
michael@0 783 { }
michael@0 784
michael@0 785 bool ensureChars(ThreadSafeContext *cx);
michael@0 786
michael@0 787 const jschar *chars() {
michael@0 788 JS_ASSERT(chars_);
michael@0 789 return chars_;
michael@0 790 }
michael@0 791
michael@0 792 JS::TwoByteChars range() {
michael@0 793 JS_ASSERT(chars_);
michael@0 794 return JS::TwoByteChars(chars_, str_->length());
michael@0 795 }
michael@0 796 };
michael@0 797
michael@0 798 class StaticStrings
michael@0 799 {
michael@0 800 private:
michael@0 801 /* Bigger chars cannot be in a length-2 string. */
michael@0 802 static const size_t SMALL_CHAR_LIMIT = 128U;
michael@0 803 static const size_t NUM_SMALL_CHARS = 64U;
michael@0 804
michael@0 805 JSAtom *length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
michael@0 806
michael@0 807 public:
michael@0 808 /* We keep these public for the JITs. */
michael@0 809 static const size_t UNIT_STATIC_LIMIT = 256U;
michael@0 810 JSAtom *unitStaticTable[UNIT_STATIC_LIMIT];
michael@0 811
michael@0 812 static const size_t INT_STATIC_LIMIT = 256U;
michael@0 813 JSAtom *intStaticTable[INT_STATIC_LIMIT];
michael@0 814
michael@0 815 StaticStrings() {
michael@0 816 mozilla::PodZero(this);
michael@0 817 }
michael@0 818
michael@0 819 bool init(JSContext *cx);
michael@0 820 void trace(JSTracer *trc);
michael@0 821
michael@0 822 static bool hasUint(uint32_t u) { return u < INT_STATIC_LIMIT; }
michael@0 823
michael@0 824 JSAtom *getUint(uint32_t u) {
michael@0 825 JS_ASSERT(hasUint(u));
michael@0 826 return intStaticTable[u];
michael@0 827 }
michael@0 828
michael@0 829 static bool hasInt(int32_t i) {
michael@0 830 return uint32_t(i) < INT_STATIC_LIMIT;
michael@0 831 }
michael@0 832
michael@0 833 JSAtom *getInt(int32_t i) {
michael@0 834 JS_ASSERT(hasInt(i));
michael@0 835 return getUint(uint32_t(i));
michael@0 836 }
michael@0 837
michael@0 838 static bool hasUnit(jschar c) { return c < UNIT_STATIC_LIMIT; }
michael@0 839
michael@0 840 JSAtom *getUnit(jschar c) {
michael@0 841 JS_ASSERT(hasUnit(c));
michael@0 842 return unitStaticTable[c];
michael@0 843 }
michael@0 844
michael@0 845 /* May not return atom, returns null on (reported) failure. */
michael@0 846 inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index);
michael@0 847
michael@0 848 static bool isStatic(JSAtom *atom);
michael@0 849
michael@0 850 /* Return null if no static atom exists for the given (chars, length). */
michael@0 851 JSAtom *lookup(const jschar *chars, size_t length) {
michael@0 852 switch (length) {
michael@0 853 case 1:
michael@0 854 if (chars[0] < UNIT_STATIC_LIMIT)
michael@0 855 return getUnit(chars[0]);
michael@0 856 return nullptr;
michael@0 857 case 2:
michael@0 858 if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
michael@0 859 return getLength2(chars[0], chars[1]);
michael@0 860 return nullptr;
michael@0 861 case 3:
michael@0 862 /*
michael@0 863 * Here we know that JSString::intStringTable covers only 256 (or at least
michael@0 864 * not 1000 or more) chars. We rely on order here to resolve the unit vs.
michael@0 865 * int string/length-2 string atom identity issue by giving priority to unit
michael@0 866 * strings for "0" through "9" and length-2 strings for "10" through "99".
michael@0 867 */
michael@0 868 JS_STATIC_ASSERT(INT_STATIC_LIMIT <= 999);
michael@0 869 if ('1' <= chars[0] && chars[0] <= '9' &&
michael@0 870 '0' <= chars[1] && chars[1] <= '9' &&
michael@0 871 '0' <= chars[2] && chars[2] <= '9') {
michael@0 872 int i = (chars[0] - '0') * 100 +
michael@0 873 (chars[1] - '0') * 10 +
michael@0 874 (chars[2] - '0');
michael@0 875
michael@0 876 if (unsigned(i) < INT_STATIC_LIMIT)
michael@0 877 return getInt(i);
michael@0 878 }
michael@0 879 return nullptr;
michael@0 880 }
michael@0 881
michael@0 882 return nullptr;
michael@0 883 }
michael@0 884
michael@0 885 private:
michael@0 886 typedef uint8_t SmallChar;
michael@0 887 static const SmallChar INVALID_SMALL_CHAR = -1;
michael@0 888
michael@0 889 static bool fitsInSmallChar(jschar c) {
michael@0 890 return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
michael@0 891 }
michael@0 892
michael@0 893 static const SmallChar toSmallChar[];
michael@0 894
michael@0 895 JSAtom *getLength2(jschar c1, jschar c2);
michael@0 896 JSAtom *getLength2(uint32_t u) {
michael@0 897 JS_ASSERT(u < 100);
michael@0 898 return getLength2('0' + u / 10, '0' + u % 10);
michael@0 899 }
michael@0 900 };
michael@0 901
michael@0 902 /*
michael@0 903 * Represents an atomized string which does not contain an index (that is, an
michael@0 904 * unsigned 32-bit value). Thus for any PropertyName propname,
michael@0 905 * ToString(ToUint32(propname)) never equals propname.
michael@0 906 *
michael@0 907 * To more concretely illustrate the utility of PropertyName, consider that it
michael@0 908 * is used to partition, in a type-safe manner, the ways to refer to a
michael@0 909 * property, as follows:
michael@0 910 *
michael@0 911 * - uint32_t indexes,
michael@0 912 * - PropertyName strings which don't encode uint32_t indexes, and
michael@0 913 * - jsspecial special properties (non-ES5 properties like object-valued
michael@0 914 * jsids, JSID_EMPTY, JSID_VOID, and maybe in the future Harmony-proposed
michael@0 915 * private names).
michael@0 916 */
michael@0 917 class PropertyName : public JSAtom
michael@0 918 {};
michael@0 919
michael@0 920 JS_STATIC_ASSERT(sizeof(PropertyName) == sizeof(JSString));
michael@0 921
michael@0 922 static MOZ_ALWAYS_INLINE jsid
michael@0 923 NameToId(PropertyName *name)
michael@0 924 {
michael@0 925 return NON_INTEGER_ATOM_TO_JSID(name);
michael@0 926 }
michael@0 927
michael@0 928 typedef HeapPtr<JSAtom> HeapPtrAtom;
michael@0 929
michael@0 930 class AutoNameVector : public AutoVectorRooter<PropertyName *>
michael@0 931 {
michael@0 932 typedef AutoVectorRooter<PropertyName *> BaseType;
michael@0 933 public:
michael@0 934 explicit AutoNameVector(JSContext *cx
michael@0 935 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
michael@0 936 : AutoVectorRooter<PropertyName *>(cx, NAMEVECTOR)
michael@0 937 {
michael@0 938 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 939 }
michael@0 940
michael@0 941 HandlePropertyName operator[](size_t i) const {
michael@0 942 return HandlePropertyName::fromMarkedLocation(&begin()[i]);
michael@0 943 }
michael@0 944
michael@0 945 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
michael@0 946 };
michael@0 947
michael@0 948 } /* namespace js */
michael@0 949
michael@0 950 /* Avoid requiring vm/String-inl.h just to call getChars. */
michael@0 951
michael@0 952 MOZ_ALWAYS_INLINE const jschar *
michael@0 953 JSString::getChars(js::ExclusiveContext *cx)
michael@0 954 {
michael@0 955 if (JSLinearString *str = ensureLinear(cx))
michael@0 956 return str->chars();
michael@0 957 return nullptr;
michael@0 958 }
michael@0 959
michael@0 960 MOZ_ALWAYS_INLINE bool
michael@0 961 JSString::getChar(js::ExclusiveContext *cx, size_t index, jschar *code)
michael@0 962 {
michael@0 963 JS_ASSERT(index < length());
michael@0 964
michael@0 965 /*
michael@0 966 * Optimization for one level deep ropes.
michael@0 967 * This is common for the following pattern:
michael@0 968 *
michael@0 969 * while() {
michael@0 970 * text = text.substr(0, x) + "bla" + text.substr(x)
michael@0 971 * test.charCodeAt(x + 1)
michael@0 972 * }
michael@0 973 */
michael@0 974 const jschar *chars;
michael@0 975 if (isRope()) {
michael@0 976 JSRope *rope = &asRope();
michael@0 977 if (uint32_t(index) < rope->leftChild()->length()) {
michael@0 978 chars = rope->leftChild()->getChars(cx);
michael@0 979 } else {
michael@0 980 chars = rope->rightChild()->getChars(cx);
michael@0 981 index -= rope->leftChild()->length();
michael@0 982 }
michael@0 983 } else {
michael@0 984 chars = getChars(cx);
michael@0 985 }
michael@0 986
michael@0 987 if (!chars)
michael@0 988 return false;
michael@0 989
michael@0 990 *code = chars[index];
michael@0 991 return true;
michael@0 992 }
michael@0 993
michael@0 994 MOZ_ALWAYS_INLINE const jschar *
michael@0 995 JSString::getCharsZ(js::ExclusiveContext *cx)
michael@0 996 {
michael@0 997 if (JSFlatString *str = ensureFlat(cx))
michael@0 998 return str->chars();
michael@0 999 return nullptr;
michael@0 1000 }
michael@0 1001
michael@0 1002 MOZ_ALWAYS_INLINE const jschar *
michael@0 1003 JSString::pureChars() const
michael@0 1004 {
michael@0 1005 JS_ASSERT(hasPureChars());
michael@0 1006 return asLinear().chars();
michael@0 1007 }
michael@0 1008
michael@0 1009 MOZ_ALWAYS_INLINE const jschar *
michael@0 1010 JSString::pureCharsZ() const
michael@0 1011 {
michael@0 1012 JS_ASSERT(hasPureCharsZ());
michael@0 1013 return asFlat().charsZ();
michael@0 1014 }
michael@0 1015
michael@0 1016 MOZ_ALWAYS_INLINE bool
michael@0 1017 JSString::copyNonPureChars(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const
michael@0 1018 {
michael@0 1019 JS_ASSERT(!hasPureChars());
michael@0 1020 return asRope().copyNonPureChars(cx, out);
michael@0 1021 }
michael@0 1022
michael@0 1023 MOZ_ALWAYS_INLINE bool
michael@0 1024 JSString::copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const
michael@0 1025 {
michael@0 1026 JS_ASSERT(!hasPureChars());
michael@0 1027 if (isDependent())
michael@0 1028 return asDependent().copyNonPureCharsZ(cx, out);
michael@0 1029 return asRope().copyNonPureCharsZ(cx, out);
michael@0 1030 }
michael@0 1031
michael@0 1032 MOZ_ALWAYS_INLINE JSLinearString *
michael@0 1033 JSString::ensureLinear(js::ExclusiveContext *cx)
michael@0 1034 {
michael@0 1035 return isLinear()
michael@0 1036 ? &asLinear()
michael@0 1037 : asRope().flatten(cx);
michael@0 1038 }
michael@0 1039
michael@0 1040 MOZ_ALWAYS_INLINE JSFlatString *
michael@0 1041 JSString::ensureFlat(js::ExclusiveContext *cx)
michael@0 1042 {
michael@0 1043 return isFlat()
michael@0 1044 ? &asFlat()
michael@0 1045 : isDependent()
michael@0 1046 ? asDependent().undepend(cx)
michael@0 1047 : asRope().flatten(cx);
michael@0 1048 }
michael@0 1049
michael@0 1050 inline JSLinearString *
michael@0 1051 JSString::base() const
michael@0 1052 {
michael@0 1053 JS_ASSERT(hasBase());
michael@0 1054 JS_ASSERT(!d.s.u2.base->isInline());
michael@0 1055 return d.s.u2.base;
michael@0 1056 }
michael@0 1057
michael@0 1058 inline js::PropertyName *
michael@0 1059 JSAtom::asPropertyName()
michael@0 1060 {
michael@0 1061 #ifdef DEBUG
michael@0 1062 uint32_t dummy;
michael@0 1063 JS_ASSERT(!isIndex(&dummy));
michael@0 1064 #endif
michael@0 1065 return static_cast<js::PropertyName *>(this);
michael@0 1066 }
michael@0 1067
michael@0 1068 #endif /* vm_String_h */

mercurial