LCOV - app.info - js/src/vm/String.h

LCOV - code coverage report
| Current view: | directory - js/src/vm - String.h (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 110 | 103 | 93.6 %
| Date: | 2012-04-07 | Functions: | 36 | 35 | 97.2 %

           1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is SpiderMonkey JavaScript engine.
          18                 :  *
          19                 :  * The Initial Developer of the Original Code is
          20                 :  * Mozilla Corporation.
          21                 :  * Portions created by the Initial Developer are Copyright (C) 2009
          22                 :  * the Initial Developer. All Rights Reserved.
          23                 :  *
          24                 :  * Contributor(s):
          25                 :  *   Luke Wagner <luke@mozilla.com>
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : #ifndef String_h_
          42                 : #define String_h_
          43                 :
          44                 : #include "mozilla/Attributes.h"
          45                 :
          46                 : #include "jsapi.h"
          47                 : #include "jscell.h"
          48                 : #include "jsfriendapi.h"
          49                 :
          50                 : class JSString;
          51                 : class JSDependentString;
          52                 : class JSExtensibleString;
          53                 : class JSExternalString;
          54                 : class JSLinearString;
          55                 : class JSFixedString;
          56                 : class JSRope;
          57                 : class JSAtom;
          58                 :
          59                 : namespace js {
          60                 :
          61                 : class StaticStrings;
          62                 : class PropertyName;
          63                 :
          64                 : /* The buffer length required to contain any unsigned 32-bit integer. */
          65                 : static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
          66                 :
          67                 : } /* namespace js */
          68                 :
          69                 : /*
          70                 :  * JavaScript strings
          71                 :  *
          72                 :  * Conceptually, a JS string is just an array of chars and a length. This array
          73                 :  * of chars may or may not be null-terminated and, if it is, the null character
          74                 :  * is not included in the length.
          75                 :  *
          76                 :  * To improve performance of common operations, the following optimizations are
          77                 :  * made which affect the engine's representation of strings:
          78                 :  *
          79                 :  *  - The plain vanilla representation is a "flat" string which consists of a
          80                 :  *    string header in the GC heap and a malloc'd null terminated char array.
          81                 :  *
          82                 :  *  - To avoid copying a substring of an existing "base" string , a "dependent"
          83                 :  *    string (JSDependentString) can be created which points into the base
          84                 :  *    string's char array.
          85                 :  *
          86                 :  *  - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created
          87                 :  *    to represent a delayed string concatenation. Concatenation (called
          88                 :  *    flattening) is performed if and when a linear char array is requested. In
          89                 :  *    general, ropes form a binary dag whose internal nodes are JSRope string
          90                 :  *    headers with no associated char array and whose leaf nodes are either flat
          91                 :  *    or dependent strings.
          92                 :  *
          93                 :  *  - To avoid copying the left-hand side when flattening, the left-hand side's
          94                 :  *    buffer may be grown to make space for a copy of the right-hand side (see
          95                 :  *    comment in JSString::flatten). This optimization requires that there are
          96                 :  *    no external pointers into the char array. We conservatively maintain this
          97                 :  *    property via a flat string's "extensible" property.
          98                 :  *
          99                 :  *  - To avoid allocating small char arrays, short strings can be stored inline
         100                 :  *    in the string header (JSInlineString). To increase the max size of such
         101                 :  *    inline strings, extra-large string headers can be used (JSShortString).
         102                 :  *
         103                 :  *  - To avoid comparing O(n) string equality comparison, strings can be
         104                 :  *    canonicalized to "atoms" (JSAtom) such that there is a single atom with a
         105                 :  *    given (length,chars).
         106                 :  *
         107                 :  *  - To avoid copying all strings created through the JSAPI, an "external"
         108                 :  *    string (JSExternalString) can be created whose chars are managed by the
         109                 :  *    JSAPI client.
         110                 :  *
         111                 :  * Although all strings share the same basic memory layout, we can conceptually
         112                 :  * arrange them into a hierarchy of operations/invariants and represent this
         113                 :  * hierarchy in C++ with classes:
         114                 :  *
         115                 :  * C++ type                     operations+fields / invariants+properties
         116                 :  *
         117                 :  * JSString (abstract)          getCharsZ, getChars, length / -
         118                 :  *  | \
         119                 :  *  | JSRope                    leftChild, rightChild / -
         120                 :  *  |
         121                 :  * JSLinearString (abstract)    chars / not null-terminated
         122                 :  *  | \
         123                 :  *  | JSDependentString         base / -
         124                 :  *  |
         125                 :  * JSFlatString (abstract)      chars / null-terminated
         126                 :  *  | \
         127                 :  *  | JSExtensibleString        capacity / no external pointers into char array
         128                 :  *  |
         129                 :  * JSFixedString                - / may have external pointers into char array
         130                 :  *  | \  \
         131                 :  *  |  \ JSExternalString       - / char array memory managed by embedding
         132                 :  *  |   \
         133                 :  *  |   JSInlineString          - / chars stored in header
         134                 :  *  |     | \
         135                 :  *  |     | JSShortString       - / header is fat
         136                 :  *  |     |        |
         137                 :  * JSAtom |        |            - / string equality === pointer equality
         138                 :  *  | \   |        |
         139                 :  *  | JSInlineAtom |            - / atomized JSInlineString
         140                 :  *  |       \      |
         141                 :  *  |       JSShortAtom         - / atomized JSShortString
         142                 :  *  |
         143                 :  * js::PropertyName             - / chars don't contain an index (uint32_t)
         144                 :  *
         145                 :  * Classes marked with (abstract) above are not literally C++ Abstract Base
         146                 :  * Classes (since there are no virtual functions, pure or not, in this
         147                 :  * hierarchy), but have the same meaning: there are no strings with this type as
         148                 :  * its most-derived type.
         149                 :  *
         150                 :  * Derived string types can be queried from ancestor types via isX() and
         151                 :  * retrieved with asX() debug-only-checked casts.
         152                 :  *
         153                 :  * The ensureX() operations mutate 'this' in place to effectively the type to be
         154                 :  * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
         155                 :  */
         156                 :
         157                 : class JSString : public js::gc::Cell
         158                 : {
         159                 :   protected:
         160                 :     static const size_t NUM_INLINE_CHARS = 2 * sizeof(void *) / sizeof(jschar);
         161                 :
         162                 :     /* Fields only apply to string types commented on the right. */
         163                 :     struct Data
         164                 :     {
         165                 :         size_t                     lengthAndFlags;      /* JSString */
         166                 :         union {
         167                 :             const jschar           *chars;              /* JSLinearString */
         168                 :             JSString               *left;               /* JSRope */
         169                 :         } u1;
         170                 :         union {
         171                 :             jschar                 inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|Short)String */
         172                 :             struct {
         173                 :                 union {
         174                 :                     JSLinearString *base;               /* JSDependentString */
         175                 :                     JSString       *right;              /* JSRope */
         176                 :                     size_t         capacity;            /* JSFlatString (extensible) */
         177                 :                     const JSStringFinalizer *externalFinalizer;/* JSExternalString */
         178                 :                 } u2;
         179                 :                 union {
         180                 :                     JSString       *parent;             /* JSRope (temporary) */
         181                 :                     size_t         reserved;            /* may use for bug 615290 */
         182                 :                 } u3;
         183                 :             } s;
         184                 :         };
         185                 :     } d;
         186                 :
         187                 :   public:
         188                 :     /* Flags exposed only for jits */
         189                 :
         190                 :     static const size_t LENGTH_SHIFT      = 4;
         191                 :     static const size_t FLAGS_MASK        = JS_BITMASK(LENGTH_SHIFT);
         192                 :     static const size_t MAX_LENGTH        = JS_BIT(32 - LENGTH_SHIFT) - 1;
         193                 :
         194                 :     /*
         195                 :      * The low LENGTH_SHIFT bits of lengthAndFlags are used to encode the type
         196                 :      * of the string. The remaining bits store the string length (which must be
         197                 :      * less or equal than MAX_LENGTH).
         198                 :      *
         199                 :      * Instead of using a dense index to represent the most-derived type, string
         200                 :      * types are encoded to allow single-op tests for hot queries (isRope,
         201                 :      * isDependent, isFlat, isAtom, isStaticAtom) which, in view of subtyping,
         202                 :      * would require slower (isX() || isY() || isZ()).
         203                 :      *
         204                 :      * The string type encoding can be summarized as follows. The "instance
         205                 :      * encoding" entry for a type specifies the flag bits used to create a
         206                 :      * string instance of that type. Abstract types have no instances and thus
         207                 :      * have no such entry. The "subtype predicate" entry for a type specifies
         208                 :      * the predicate used to query whether a JSString instance is subtype
         209                 :      * (reflexively) of that type.
         210                 :      *
         211                 :      *   string       instance   subtype
         212                 :      *   type         encoding   predicate
         213                 :      *
         214                 :      *   String       -          true
         215                 :      *   Rope         0001       xxx1
         216                 :      *   Linear       -          xxx0
         217                 :      *   Dependent    0010       xx1x
         218                 :      *   Flat         -          xx00
         219                 :      *   Extensible   1100       1100
         220                 :      *   Fixed        0100       isFlat && !isExtensible
         221                 :      *   Inline       0100       isFixed && (u1.chars == inlineStorage || isShort)
         222                 :      *   Short        0100       xxxx && header in FINALIZE_SHORT_STRING arena
         223                 :      *   External     0100       xxxx && header in FINALIZE_EXTERNAL_STRING arena
         224                 :      *   Atom         1000       x000
         225                 :      *   InlineAtom   1000       1000 && is Inline
         226                 :      *   ShortAtom    1000       1000 && is Short
         227                 :      *   StaticAtom   0000       0000
         228                 :      */
         229                 :
         230                 :     static const size_t ROPE_BIT          = JS_BIT(0);
         231                 :
         232                 :     static const size_t LINEAR_MASK       = JS_BITMASK(1);
         233                 :     static const size_t LINEAR_FLAGS      = 0x0;
         234                 :
         235                 :     static const size_t DEPENDENT_BIT     = JS_BIT(1);
         236                 :
         237                 :     static const size_t FLAT_MASK         = JS_BITMASK(2);
         238                 :     static const size_t FLAT_FLAGS        = 0x0;
         239                 :
         240                 :     static const size_t FIXED_FLAGS       = JS_BIT(2);
         241                 :
         242                 :     static const size_t ATOM_MASK         = JS_BITMASK(3);
         243                 :     static const size_t ATOM_FLAGS        = 0x0;
         244                 :
         245                 :     static const size_t EXTENSIBLE_FLAGS  = JS_BIT(2) | JS_BIT(3);
         246                 :     static const size_t NON_STATIC_ATOM   = JS_BIT(3);
         247                 :
         248       162621678 :     size_t buildLengthAndFlags(size_t length, size_t flags) {
         249       162621678 :         return (length << LENGTH_SHIFT) | flags;
         250                 :     }
         251                 :
         252                 :     /*
         253                 :      * Helper function to validate that a string of a given length is
         254                 :      * representable by a JSString. An allocation overflow is reported if false
         255                 :      * is returned.
         256                 :      */
         257                 :     static inline bool validateLength(JSContext *cx, size_t length);
         258                 :
         259                 :     static void staticAsserts() {
         260                 :         JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
         261                 :         JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >>
         262                 :                            JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH);
         263                 :         JS_STATIC_ASSERT(sizeof(JSString) ==
         264                 :                          offsetof(JSString, d.inlineStorage) + NUM_INLINE_CHARS * sizeof(jschar));
         265                 :         JS_STATIC_ASSERT(offsetof(JSString, d.u1.chars) ==
         266                 :                          offsetof(js::shadow::Atom, chars));
         267                 :     }
         268                 :
         269                 :     /* Avoid lame compile errors in JSRope::flatten */
         270                 :     friend class JSRope;
         271                 :
         272                 :   public:
         273                 :     /* All strings have length. */
         274                 :
         275                 :     JS_ALWAYS_INLINE
         276       136997728 :     size_t length() const {
         277       136997728 :         return d.lengthAndFlags >> LENGTH_SHIFT;
         278                 :     }
         279                 :
         280                 :     JS_ALWAYS_INLINE
         281           10622 :     bool empty() const {
         282           10622 :         return d.lengthAndFlags <= FLAGS_MASK;
         283                 :     }
         284                 :
         285                 :     /*
         286                 :      * All strings have a fallible operation to get an array of chars.
         287                 :      * getCharsZ additionally ensures the array is null terminated.
         288                 :      */
         289                 :
         290                 :     inline const jschar *getChars(JSContext *cx);
         291                 :     inline const jschar *getCharsZ(JSContext *cx);
         292                 :
         293                 :     /* Fallible conversions to more-derived string types. */
         294                 :
         295                 :     inline JSLinearString *ensureLinear(JSContext *cx);
         296                 :     inline JSFlatString *ensureFlat(JSContext *cx);
         297                 :     inline JSFixedString *ensureFixed(JSContext *cx);
         298                 :
         299                 :     /* Type query and debug-checked casts */
         300                 :
         301                 :     JS_ALWAYS_INLINE
         302       298272787 :     bool isRope() const {
         303       298272787 :         bool rope = d.lengthAndFlags & ROPE_BIT;
         304       298272787 :         JS_ASSERT_IF(rope, (d.lengthAndFlags & FLAGS_MASK) == ROPE_BIT);
         305       298272787 :         return rope;
         306                 :     }
         307                 :
         308                 :     JS_ALWAYS_INLINE
         309        46340243 :     JSRope &asRope() const {
         310        46340243 :         JS_ASSERT(isRope());
         311        46340243 :         return *(JSRope *)this;
         312                 :     }
         313                 :
         314                 :     JS_ALWAYS_INLINE
         315       992985932 :     bool isLinear() const {
         316       992985932 :         return (d.lengthAndFlags & LINEAR_MASK) == LINEAR_FLAGS;
         317                 :     }
         318                 :
         319                 :     JS_ALWAYS_INLINE
         320       257333820 :     JSLinearString &asLinear() const {
         321       257333820 :         JS_ASSERT(JSString::isLinear());
         322       257333820 :         return *(JSLinearString *)this;
         323                 :     }
         324                 :
         325                 :     JS_ALWAYS_INLINE
         326       235644102 :     bool isDependent() const {
         327       235644102 :         bool dependent = d.lengthAndFlags & DEPENDENT_BIT;
         328       235644102 :         JS_ASSERT_IF(dependent, (d.lengthAndFlags & FLAGS_MASK) == DEPENDENT_BIT);
         329       235644102 :         return dependent;
         330                 :     }
         331                 :
         332                 :     JS_ALWAYS_INLINE
         333          495174 :     JSDependentString &asDependent() const {
         334          495174 :         JS_ASSERT(isDependent());
         335          495174 :         return *(JSDependentString *)this;
         336                 :     }
         337                 :
         338                 :     JS_ALWAYS_INLINE
         339       446421612 :     bool isFlat() const {
         340       446421612 :         return (d.lengthAndFlags & FLAT_MASK) == FLAT_FLAGS;
         341                 :     }
         342                 :
         343                 :     JS_ALWAYS_INLINE
         344        88739954 :     JSFlatString &asFlat() const {
         345        88739954 :         JS_ASSERT(isFlat());
         346        88739943 :         return *(JSFlatString *)this;
         347                 :     }
         348                 :
         349                 :     JS_ALWAYS_INLINE
         350         1381715 :     bool isExtensible() const {
         351         1381715 :         return (d.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS;
         352                 :     }
         353                 :
         354                 :     JS_ALWAYS_INLINE
         355           90504 :     JSExtensibleString &asExtensible() const {
         356           90504 :         JS_ASSERT(isExtensible());
         357           90504 :         return *(JSExtensibleString *)this;
         358                 :     }
         359                 :
         360                 :     /* For hot code, prefer other type queries. */
         361                 :     bool isShort() const;
         362                 :     bool isFixed() const;
         363                 :     bool isInline() const;
         364                 :
         365                 :     JS_ALWAYS_INLINE
         366          363303 :     JSFixedString &asFixed() const {
         367          363303 :         JS_ASSERT(isFixed());
         368          363303 :         return *(JSFixedString *)this;
         369                 :     }
         370                 :
         371                 :     bool isExternal() const;
         372                 :
         373                 :     JS_ALWAYS_INLINE
         374               0 :     JSExternalString &asExternal() const {
         375               0 :         JS_ASSERT(isExternal());
         376               0 :         return *(JSExternalString *)this;
         377                 :     }
         378                 :
         379                 :     JS_ALWAYS_INLINE
         380       206678935 :     bool isAtom() const {
         381       206678935 :         bool atomized = (d.lengthAndFlags & ATOM_MASK) == ATOM_FLAGS;
         382       206678935 :         JS_ASSERT_IF(atomized, isFlat());
         383       206678935 :         return atomized;
         384                 :     }
         385                 :
         386                 :     JS_ALWAYS_INLINE
         387       117118637 :     JSAtom &asAtom() const {
         388       117118637 :         JS_ASSERT(isAtom());
         389       117118637 :         return *(JSAtom *)this;
         390                 :     }
         391                 :
         392                 :     /* Only called by the GC for strings with the FINALIZE_STRING kind. */
         393                 :
         394                 :     inline void finalize(js::FreeOp *fop);
         395                 :
         396                 :     /* Gets the number of bytes that the chars take on the heap. */
         397                 :
         398                 :     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf);
         399                 :
         400                 :     /* Offsets for direct field from jit code. */
         401                 :
         402            1663 :     static size_t offsetOfLengthAndFlags() {
         403            1663 :         return offsetof(JSString, d.lengthAndFlags);
         404                 :     }
         405                 :
         406             105 :     static size_t offsetOfChars() {
         407             105 :         return offsetof(JSString, d.u1.chars);
         408                 :     }
         409                 :
         410                 :     static inline void writeBarrierPre(JSString *str);
         411                 :     static inline void writeBarrierPost(JSString *str, void *addr);
         412                 :     static inline bool needWriteBarrierPre(JSCompartment *comp);
         413                 :     static inline void readBarrier(JSString *str);
         414                 :
         415                 :     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; }
         416                 :
         417                 : #ifdef DEBUG
         418                 :     void dump();
         419                 :     bool equals(const char *s);
         420                 : #endif
         421                 : };
         422                 :
         423                 : class JSRope : public JSString
         424                 : {
         425                 :     enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
         426                 :     template<UsingBarrier b>
         427                 :     JSFlatString *flattenInternal(JSContext *cx);
         428                 :
         429                 :     friend class JSString;
         430                 :     JSFlatString *flatten(JSContext *cx);
         431                 :
         432                 :     void init(JSString *left, JSString *right, size_t length);
         433                 :
         434                 :   public:
         435                 :     static inline JSRope *new_(JSContext *cx, JSString *left,
         436                 :                                JSString *right, size_t length);
         437                 :
         438        46428575 :     inline JSString *leftChild() const {
         439        46428575 :         JS_ASSERT(isRope());
         440        46428575 :         return d.u1.left;
         441                 :     }
         442                 :
         443        45865970 :     inline JSString *rightChild() const {
         444        45865970 :         JS_ASSERT(isRope());
         445        45865970 :         return d.s.u2.right;
         446                 :     }
         447                 :
         448                 :     inline void markChildren(JSTracer *trc);
         449                 : };
         450                 :
         451                 : JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
         452                 :
         453                 : class JSLinearString : public JSString
         454                 : {
         455                 :     friend class JSString;
         456                 :
         457                 :     /* Vacuous and therefore unimplemented. */
         458                 :     JSLinearString *ensureLinear(JSContext *cx) MOZ_DELETE;
         459                 :     bool isLinear() const MOZ_DELETE;
         460                 :     JSLinearString &asLinear() const MOZ_DELETE;
         461                 :
         462                 :   public:
         463                 :     JS_ALWAYS_INLINE
         464       317818207 :     const jschar *chars() const {
         465       317818207 :         JS_ASSERT(JSString::isLinear());
         466       317818158 :         return d.u1.chars;
         467                 :     }
         468                 : };
         469                 :
         470                 : JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString));
         471                 :
         472                 : class JSDependentString : public JSLinearString
         473                 : {
         474                 :     friend class JSString;
         475                 :     JSFixedString *undepend(JSContext *cx);
         476                 :
         477                 :     void init(JSLinearString *base, const jschar *chars, size_t length);
         478                 :
         479                 :     /* Vacuous and therefore unimplemented. */
         480                 :     bool isDependent() const MOZ_DELETE;
         481                 :     JSDependentString &asDependent() const MOZ_DELETE;
         482                 :
         483                 :   public:
         484                 :     static inline JSDependentString *new_(JSContext *cx, JSLinearString *base,
         485                 :                                           const jschar *chars, size_t length);
         486                 :
         487          494174 :     JSLinearString *base() const {
         488          494174 :         JS_ASSERT(JSString::isDependent());
         489          494174 :         return d.s.u2.base;
         490                 :     }
         491                 :
         492                 :     inline void markChildren(JSTracer *trc);
         493                 : };
         494                 :
         495                 : JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
         496                 :
         497                 : class JSFlatString : public JSLinearString
         498                 : {
         499                 :     friend class JSRope;
         500                 :     void morphExtensibleIntoDependent(JSLinearString *base) {
         501                 :         d.lengthAndFlags = buildLengthAndFlags(length(), DEPENDENT_BIT);
         502                 :         d.s.u2.base = base;
         503                 :     }
         504                 :
         505                 :     /* Vacuous and therefore unimplemented. */
         506                 :     JSFlatString *ensureFlat(JSContext *cx) MOZ_DELETE;
         507                 :     bool isFlat() const MOZ_DELETE;
         508                 :     JSFlatString &asFlat() const MOZ_DELETE;
         509                 :
         510                 :   public:
         511                 :     JS_ALWAYS_INLINE
         512        23610896 :     const jschar *charsZ() const {
         513        23610896 :         JS_ASSERT(JSString::isFlat());
         514        23610896 :         return chars();
         515                 :     }
         516                 :
         517                 :     /*
         518                 :      * Returns true if this string's characters store an unsigned 32-bit
         519                 :      * integer value, initializing *indexp to that value if so.  (Thus if
         520                 :      * calling isIndex returns true, js::IndexToString(cx, *indexp) will be a
         521                 :      * string equal to this string.)
         522                 :      */
         523                 :     bool isIndex(uint32_t *indexp) const;
         524                 :
         525                 :     /*
         526                 :      * Returns a property name represented by this string, or null on failure.
         527                 :      * You must verify that this is not an index per isIndex before calling
         528                 :      * this method.
         529                 :      */
         530                 :     inline js::PropertyName *toPropertyName(JSContext *cx);
         531                 :
         532                 :     inline void finalize(js::FreeOp *fop);
         533                 : };
         534                 :
         535                 : JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
         536                 :
         537                 : class JSExtensibleString : public JSFlatString
         538                 : {
         539                 :     /* Vacuous and therefore unimplemented. */
         540                 :     bool isExtensible() const MOZ_DELETE;
         541                 :     JSExtensibleString &asExtensible() const MOZ_DELETE;
         542                 :
         543                 :   public:
         544                 :     JS_ALWAYS_INLINE
         545           90504 :     size_t capacity() const {
         546           90504 :         JS_ASSERT(JSString::isExtensible());
         547           90504 :         return d.s.u2.capacity;
         548                 :     }
         549                 : };
         550                 :
         551                 : JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
         552                 :
         553                 : class JSFixedString : public JSFlatString
         554                 : {
         555                 :     void init(const jschar *chars, size_t length);
         556                 :
         557                 :     /* Vacuous and therefore unimplemented. */
         558                 :     JSFlatString *ensureFixed(JSContext *cx) MOZ_DELETE;
         559                 :     bool isFixed() const MOZ_DELETE;
         560                 :     JSFixedString &asFixed() const MOZ_DELETE;
         561                 :
         562                 :   public:
         563                 :     static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length);
         564                 :
         565                 :     /*
         566                 :      * Once a JSFixedString has been added to the atom state, this operation
         567                 :      * changes the type (in place, as reflected by the flag bits) of the
         568                 :      * JSFixedString into a JSAtom.
         569                 :      */
         570                 :     inline JSAtom *morphAtomizedStringIntoAtom();
         571                 : };
         572                 :
         573                 : JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString));
         574                 :
         575                 : class JSInlineString : public JSFixedString
         576                 : {
         577                 :     static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
         578                 :
         579                 :   public:
         580                 :     static inline JSInlineString *new_(JSContext *cx);
         581                 :
         582                 :     inline jschar *init(size_t length);
         583                 :
         584                 :     inline void resetLength(size_t length);
         585                 :
         586       185382439 :     static bool lengthFits(size_t length) {
         587       185382439 :         return length <= MAX_INLINE_LENGTH;
         588                 :     }
         589                 :
         590                 : };
         591                 :
         592                 : JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
         593                 :
         594                 : class JSShortString : public JSInlineString
         595                 : {
         596                 :     /* This can be any value that is a multiple of Cell::CellSize. */
         597                 :     static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
         598                 :
         599                 :     static void staticAsserts() {
         600                 :         JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % js::gc::Cell::CellSize == 0);
         601                 :         JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
         602                 :                          (sizeof(JSShortString) -
         603                 :                           offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));
         604                 :     }
         605                 :
         606                 :     jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
         607                 :
         608                 :   public:
         609                 :     static inline JSShortString *new_(JSContext *cx);
         610                 :
         611        38815626 :     jschar *inlineStorageBeforeInit() {
         612        38815626 :         return d.inlineStorage;
         613                 :     }
         614                 :
         615                 :     inline void initAtOffsetInBuffer(const jschar *chars, size_t length);
         616                 :
         617                 :     static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS +
         618                 :                                            INLINE_EXTENSION_CHARS
         619                 :                                            -1 /* null terminator */;
         620                 :
         621       253309595 :     static bool lengthFits(size_t length) {
         622       253309595 :         return length <= MAX_SHORT_LENGTH;
         623                 :     }
         624                 :
         625                 :     /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
         626                 :
         627                 :     JS_ALWAYS_INLINE void finalize(js::FreeOp *fop);
         628                 : };
         629                 :
         630                 : JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
         631                 :
         632                 : class JSExternalString : public JSFixedString
         633                 : {
         634                 :     void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
         635                 :
         636                 :     /* Vacuous and therefore unimplemented. */
         637                 :     bool isExternal() const MOZ_DELETE;
         638                 :     JSExternalString &asExternal() const MOZ_DELETE;
         639                 :
         640                 :   public:
         641                 :     static inline JSExternalString *new_(JSContext *cx, const jschar *chars, size_t length,
         642                 :                                          const JSStringFinalizer *fin);
         643                 :
         644            2000 :     const JSStringFinalizer *externalFinalizer() const {
         645            2000 :         JS_ASSERT(JSString::isExternal());
         646            2000 :         return d.s.u2.externalFinalizer;
         647                 :     }
         648                 :
         649                 :     /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
         650                 :
         651                 :     inline void finalize(js::FreeOp *fop);
         652                 : };
         653                 :
         654                 : JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
         655                 :
         656                 : class JSAtom : public JSFixedString
         657                 : {
         658                 :     /* Vacuous and therefore unimplemented. */
         659                 :     bool isAtom() const MOZ_DELETE;
         660                 :     JSAtom &asAtom() const MOZ_DELETE;
         661                 :
         662                 :   public:
         663                 :     /* Returns the PropertyName for this.  isIndex() must be false. */
         664                 :     inline js::PropertyName *asPropertyName();
         665                 :
         666                 :     inline void finalize(js::FreeOp *fop);
         667                 :
         668                 : #ifdef DEBUG
         669                 :     void dump();
         670                 : #endif
         671                 : };
         672                 :
         673                 : JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
         674                 :
         675                 : class JSInlineAtom : public JSInlineString /*, JSAtom */
         676                 : {
         677                 :     /*
         678                 :      * JSInlineAtom is not explicitly used and is only present for consistency.
         679                 :      * See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms.
         680                 :      */
         681                 : };
         682                 :
         683                 : JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString));
         684                 :
         685                 : class JSShortAtom : public JSShortString /*, JSInlineAtom */
         686                 : {
         687                 :     /*
         688                 :      * JSShortAtom is not explicitly used and is only present for consistency.
         689                 :      * See Atomize() for how JSShortStrings get morphed into JSShortAtoms.
         690                 :      */
         691                 : };
         692                 :
         693                 : JS_STATIC_ASSERT(sizeof(JSShortAtom) == sizeof(JSShortString));
         694                 :
         695                 : namespace js {
         696                 :
         697                 : class StaticStrings
         698                 : {
         699                 :   private:
         700                 :     /* Bigger chars cannot be in a length-2 string. */
         701                 :     static const size_t SMALL_CHAR_LIMIT    = 128U;
         702                 :     static const size_t NUM_SMALL_CHARS     = 64U;
         703                 :
         704                 :     static const size_t INT_STATIC_LIMIT    = 256U;
         705                 :
         706                 :     JSAtom *length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
         707                 :     JSAtom *intStaticTable[INT_STATIC_LIMIT];
         708                 :
         709                 :   public:
         710                 :     /* We keep these public for the methodjit. */
         711                 :     static const size_t UNIT_STATIC_LIMIT   = 256U;
         712                 :     JSAtom *unitStaticTable[UNIT_STATIC_LIMIT];
         713                 :
         714           18761 :     StaticStrings() {
         715           18761 :         PodArrayZero(unitStaticTable);
         716           18761 :         PodArrayZero(length2StaticTable);
         717           18761 :         PodArrayZero(intStaticTable);
         718           18761 :     }
         719                 :
         720                 :     bool init(JSContext *cx);
         721                 :     void trace(JSTracer *trc);
         722                 :
         723                 :     static inline bool hasUint(uint32_t u);
         724                 :     inline JSAtom *getUint(uint32_t u);
         725                 :
         726                 :     static inline bool hasInt(int32_t i);
         727                 :     inline JSAtom *getInt(int32_t i);
         728                 :
         729                 :     static inline bool hasUnit(jschar c);
         730                 :     JSAtom *getUnit(jschar c);
         731                 :
         732                 :     /* May not return atom, returns null on (reported) failure. */
         733                 :     inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index);
         734                 :
         735                 :     static bool isStatic(JSAtom *atom);
         736                 :
         737                 :     /* Return null if no static atom exists for the given (chars, length). */
         738                 :     inline JSAtom *lookup(const jschar *chars, size_t length);
         739                 :
         740                 :   private:
         741                 :     typedef uint8_t SmallChar;
         742                 :     static const SmallChar INVALID_SMALL_CHAR = -1;
         743                 :
         744                 :     static inline bool fitsInSmallChar(jschar c);
         745                 :
         746                 :     static const SmallChar toSmallChar[];
         747                 :
         748                 :     JSAtom *getLength2(jschar c1, jschar c2);
         749                 :     JSAtom *getLength2(uint32_t u);
         750                 : };
         751                 :
         752                 : /*
         753                 :  * Represents an atomized string which does not contain an index (that is, an
         754                 :  * unsigned 32-bit value).  Thus for any PropertyName propname,
         755                 :  * ToString(ToUint32(propname)) never equals propname.
         756                 :  *
         757                 :  * To more concretely illustrate the utility of PropertyName, consider that it
         758                 :  * is used to partition, in a type-safe manner, the ways to refer to a
         759                 :  * property, as follows:
         760                 :  *
         761                 :  *   - uint32_t indexes,
         762                 :  *   - PropertyName strings which don't encode uint32_t indexes, and
         763                 :  *   - jsspecial special properties (non-ES5 properties like object-valued
         764                 :  *     jsids, JSID_EMPTY, JSID_VOID, E4X's default XML namespace, and maybe in
         765                 :  *     the future Harmony-proposed private names).
         766                 :  */
         767                 : class PropertyName : public JSAtom
         768                 : {};
         769                 :
         770                 : JS_STATIC_ASSERT(sizeof(PropertyName) == sizeof(JSString));
         771                 :
         772                 : } /* namespace js */
         773                 :
         774                 : /* Avoid requiring vm/String-inl.h just to call getChars. */
         775                 :
         776                 : JS_ALWAYS_INLINE const jschar *
         777         5698629 : JSString::getChars(JSContext *cx)
         778                 : {
         779         5698629 :     if (JSLinearString *str = ensureLinear(cx))
         780         5698629 :         return str->chars();
         781               0 :     return NULL;
         782                 : }
         783                 :
         784                 : JS_ALWAYS_INLINE const jschar *
         785        41507326 : JSString::getCharsZ(JSContext *cx)
         786                 : {
         787        41507326 :     if (JSFlatString *str = ensureFlat(cx))
         788        41507326 :         return str->chars();
         789               0 :     return NULL;
         790                 : }
         791                 :
         792                 : JS_ALWAYS_INLINE JSLinearString *
         793        12967456 : JSString::ensureLinear(JSContext *cx)
         794                 : {
         795        12967456 :     return isLinear()
         796                 :            ? &asLinear()
         797        12967456 :            : asRope().flatten(cx);
         798                 : }
         799                 :
         800                 : JS_ALWAYS_INLINE JSFlatString *
         801        80736093 : JSString::ensureFlat(JSContext *cx)
         802                 : {
         803        80736093 :     return isFlat()
         804                 :            ? &asFlat()
         805              36 :            : isDependent()
         806               0 :              ? asDependent().undepend(cx)
         807        80736129 :              : asRope().flatten(cx);
         808                 : }
         809                 :
         810                 : JS_ALWAYS_INLINE JSFixedString *
         811          363303 : JSString::ensureFixed(JSContext *cx)
         812                 : {
         813          363303 :     if (!ensureFlat(cx))
         814               0 :         return NULL;
         815          363303 :     if (isExtensible()) {
         816              45 :         JS_ASSERT((d.lengthAndFlags & FLAT_MASK) == 0);
         817                 :         JS_STATIC_ASSERT(EXTENSIBLE_FLAGS == (JS_BIT(2) | JS_BIT(3)));
         818                 :         JS_STATIC_ASSERT(FIXED_FLAGS == JS_BIT(2));
         819              45 :         d.lengthAndFlags ^= JS_BIT(3);
         820                 :     }
         821          363303 :     return &asFixed();
         822                 : }
         823                 :
         824                 : inline js::PropertyName *
         825        20368677 : JSAtom::asPropertyName()
         826                 : {
         827                 : #ifdef DEBUG
         828                 :     uint32_t dummy;
         829        20368677 :     JS_ASSERT(!isIndex(&dummy));
         830                 : #endif
         831        20368677 :     return static_cast<js::PropertyName *>(this);
         832                 : }
         833                 :
         834                 : #endif

Generated by: LCOV version 1.7