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 190060403 : size_t buildLengthAndFlags(size_t length, size_t flags) {
249 190060403 : 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 244776831 : size_t length() const {
277 244776831 : return d.lengthAndFlags >> LENGTH_SHIFT;
278 : }
279 :
280 : JS_ALWAYS_INLINE
281 11100 : bool empty() const {
282 11100 : 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 308452020 : bool isRope() const {
303 308452020 : bool rope = d.lengthAndFlags & ROPE_BIT;
304 308452020 : JS_ASSERT_IF(rope, (d.lengthAndFlags & FLAGS_MASK) == ROPE_BIT);
305 308452020 : return rope;
306 : }
307 :
308 : JS_ALWAYS_INLINE
309 47600338 : JSRope &asRope() const {
310 47600338 : JS_ASSERT(isRope());
311 47600338 : return *(JSRope *)this;
312 : }
313 :
314 : JS_ALWAYS_INLINE
315 1721676281 : bool isLinear() const {
316 1721676281 : return (d.lengthAndFlags & LINEAR_MASK) == LINEAR_FLAGS;
317 : }
318 :
319 : JS_ALWAYS_INLINE
320 403519587 : JSLinearString &asLinear() const {
321 403519587 : JS_ASSERT(JSString::isLinear());
322 403519587 : return *(JSLinearString *)this;
323 : }
324 :
325 : JS_ALWAYS_INLINE
326 356679946 : bool isDependent() const {
327 356679946 : bool dependent = d.lengthAndFlags & DEPENDENT_BIT;
328 356679946 : JS_ASSERT_IF(dependent, (d.lengthAndFlags & FLAGS_MASK) == DEPENDENT_BIT);
329 356679946 : return dependent;
330 : }
331 :
332 : JS_ALWAYS_INLINE
333 558926 : JSDependentString &asDependent() const {
334 558926 : JS_ASSERT(isDependent());
335 558926 : return *(JSDependentString *)this;
336 : }
337 :
338 : JS_ALWAYS_INLINE
339 620365452 : bool isFlat() const {
340 620365452 : return (d.lengthAndFlags & FLAT_MASK) == FLAT_FLAGS;
341 : }
342 :
343 : JS_ALWAYS_INLINE
344 102785826 : JSFlatString &asFlat() const {
345 102785826 : JS_ASSERT(isFlat());
346 102785818 : return *(JSFlatString *)this;
347 : }
348 :
349 : JS_ALWAYS_INLINE
350 3598421 : bool isExtensible() const {
351 3598421 : return (d.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS;
352 : }
353 :
354 : JS_ALWAYS_INLINE
355 91013 : JSExtensibleString &asExtensible() const {
356 91013 : JS_ASSERT(isExtensible());
357 91013 : 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 594962 : JSFixedString &asFixed() const {
367 594962 : JS_ASSERT(isFixed());
368 594962 : 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 283439741 : bool isAtom() const {
381 283439741 : bool atomized = (d.lengthAndFlags & ATOM_MASK) == ATOM_FLAGS;
382 283439741 : JS_ASSERT_IF(atomized, isFlat());
383 283439743 : return atomized;
384 : }
385 :
386 : JS_ALWAYS_INLINE
387 138272490 : JSAtom &asAtom() const {
388 138272490 : JS_ASSERT(isAtom());
389 138272490 : return *(JSAtom *)this;
390 : }
391 :
392 : /* Only called by the GC for strings with the FINALIZE_STRING kind. */
393 :
394 : inline void finalize(JSContext *cx, bool background);
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 13279 : static size_t offsetOfLengthAndFlags() {
403 13279 : 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 47688760 : inline JSString *leftChild() const {
439 47688760 : JS_ASSERT(isRope());
440 47688760 : return d.u1.left;
441 : }
442 :
443 46481589 : inline JSString *rightChild() const {
444 46481589 : JS_ASSERT(isRope());
445 46481589 : 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 635447626 : const jschar *chars() const {
465 635447626 : JS_ASSERT(JSString::isLinear());
466 635446732 : 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 557907 : JSLinearString *base() const {
488 557907 : JS_ASSERT(JSString::isDependent());
489 557907 : 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 104418805 : const jschar *charsZ() const {
513 104418805 : JS_ASSERT(JSString::isFlat());
514 104418805 : 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 : /* Only called by the GC for strings with the FINALIZE_STRING kind. */
533 :
534 : inline void finalize(JSRuntime *rt);
535 : };
536 :
537 : JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
538 :
539 : class JSExtensibleString : public JSFlatString
540 : {
541 : /* Vacuous and therefore unimplemented. */
542 : bool isExtensible() const MOZ_DELETE;
543 : JSExtensibleString &asExtensible() const MOZ_DELETE;
544 :
545 : public:
546 : JS_ALWAYS_INLINE
547 90576 : size_t capacity() const {
548 90576 : JS_ASSERT(JSString::isExtensible());
549 90576 : return d.s.u2.capacity;
550 : }
551 : };
552 :
553 : JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
554 :
555 : class JSFixedString : public JSFlatString
556 : {
557 : void init(const jschar *chars, size_t length);
558 :
559 : /* Vacuous and therefore unimplemented. */
560 : JSFlatString *ensureFixed(JSContext *cx) MOZ_DELETE;
561 : bool isFixed() const MOZ_DELETE;
562 : JSFixedString &asFixed() const MOZ_DELETE;
563 :
564 : public:
565 : static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length);
566 :
567 : /*
568 : * Once a JSFixedString has been added to the atom state, this operation
569 : * changes the type (in place, as reflected by the flag bits) of the
570 : * JSFixedString into a JSAtom.
571 : */
572 : inline JSAtom *morphAtomizedStringIntoAtom();
573 : };
574 :
575 : JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString));
576 :
577 : class JSInlineString : public JSFixedString
578 : {
579 : static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
580 :
581 : public:
582 : static inline JSInlineString *new_(JSContext *cx);
583 :
584 : inline jschar *init(size_t length);
585 :
586 : inline void resetLength(size_t length);
587 :
588 201797776 : static bool lengthFits(size_t length) {
589 201797776 : return length <= MAX_INLINE_LENGTH;
590 : }
591 :
592 : };
593 :
594 : JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
595 :
596 : class JSShortString : public JSInlineString
597 : {
598 : /* This can be any value that is a multiple of Cell::CellSize. */
599 : static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
600 :
601 : static void staticAsserts() {
602 : JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % js::gc::Cell::CellSize == 0);
603 : JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
604 : (sizeof(JSShortString) -
605 : offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));
606 : }
607 :
608 : jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
609 :
610 : public:
611 : static inline JSShortString *new_(JSContext *cx);
612 :
613 42428174 : jschar *inlineStorageBeforeInit() {
614 42428174 : return d.inlineStorage;
615 : }
616 :
617 : inline void initAtOffsetInBuffer(const jschar *chars, size_t length);
618 :
619 : static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS +
620 : INLINE_EXTENSION_CHARS
621 : -1 /* null terminator */;
622 :
623 284424022 : static bool lengthFits(size_t length) {
624 284424022 : return length <= MAX_SHORT_LENGTH;
625 : }
626 :
627 : /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
628 :
629 : JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
630 : };
631 :
632 : JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
633 :
634 : class JSExternalString : public JSFixedString
635 : {
636 : void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
637 :
638 : /* Vacuous and therefore unimplemented. */
639 : bool isExternal() const MOZ_DELETE;
640 : JSExternalString &asExternal() const MOZ_DELETE;
641 :
642 : public:
643 : static inline JSExternalString *new_(JSContext *cx, const jschar *chars, size_t length,
644 : const JSStringFinalizer *fin);
645 :
646 977008 : const JSStringFinalizer *externalFinalizer() const {
647 977008 : JS_ASSERT(JSString::isExternal());
648 977008 : return d.s.u2.externalFinalizer;
649 : }
650 :
651 : /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
652 :
653 : inline void finalize(JSContext *cx, bool background);
654 : inline void finalize();
655 : };
656 :
657 : JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
658 :
659 : class JSAtom : public JSFixedString
660 : {
661 : /* Vacuous and therefore unimplemented. */
662 : bool isAtom() const MOZ_DELETE;
663 : JSAtom &asAtom() const MOZ_DELETE;
664 :
665 : public:
666 : /* Returns the PropertyName for this. isIndex() must be false. */
667 : inline js::PropertyName *asPropertyName();
668 :
669 : inline void finalize(JSRuntime *rt);
670 :
671 : #ifdef DEBUG
672 : void dump();
673 : #endif
674 : };
675 :
676 : JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
677 :
678 : class JSInlineAtom : public JSInlineString /*, JSAtom */
679 : {
680 : /*
681 : * JSInlineAtom is not explicitly used and is only present for consistency.
682 : * See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms.
683 : */
684 : };
685 :
686 : JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString));
687 :
688 : class JSShortAtom : public JSShortString /*, JSInlineAtom */
689 : {
690 : /*
691 : * JSShortAtom is not explicitly used and is only present for consistency.
692 : * See Atomize() for how JSShortStrings get morphed into JSShortAtoms.
693 : */
694 : };
695 :
696 : JS_STATIC_ASSERT(sizeof(JSShortAtom) == sizeof(JSShortString));
697 :
698 : namespace js {
699 :
700 : class StaticStrings
701 : {
702 : private:
703 : /* Bigger chars cannot be in a length-2 string. */
704 : static const size_t SMALL_CHAR_LIMIT = 128U;
705 : static const size_t NUM_SMALL_CHARS = 64U;
706 :
707 : static const size_t INT_STATIC_LIMIT = 256U;
708 :
709 : JSAtom *length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
710 : JSAtom *intStaticTable[INT_STATIC_LIMIT];
711 :
712 : public:
713 : /* We keep these public for the methodjit. */
714 : static const size_t UNIT_STATIC_LIMIT = 256U;
715 : JSAtom *unitStaticTable[UNIT_STATIC_LIMIT];
716 :
717 19910 : StaticStrings() {
718 19910 : PodArrayZero(unitStaticTable);
719 19910 : PodArrayZero(length2StaticTable);
720 19910 : PodArrayZero(intStaticTable);
721 19910 : }
722 :
723 : bool init(JSContext *cx);
724 : void trace(JSTracer *trc);
725 :
726 : static inline bool hasUint(uint32_t u);
727 : inline JSAtom *getUint(uint32_t u);
728 :
729 : static inline bool hasInt(int32_t i);
730 : inline JSAtom *getInt(int32_t i);
731 :
732 : static inline bool hasUnit(jschar c);
733 : JSAtom *getUnit(jschar c);
734 :
735 : /* May not return atom, returns null on (reported) failure. */
736 : inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index);
737 :
738 : static bool isStatic(JSAtom *atom);
739 :
740 : /* Return null if no static atom exists for the given (chars, length). */
741 : inline JSAtom *lookup(const jschar *chars, size_t length);
742 :
743 : private:
744 : typedef uint8_t SmallChar;
745 : static const SmallChar INVALID_SMALL_CHAR = -1;
746 :
747 : static inline bool fitsInSmallChar(jschar c);
748 :
749 : static const SmallChar toSmallChar[];
750 :
751 : JSAtom *getLength2(jschar c1, jschar c2);
752 : JSAtom *getLength2(uint32_t u);
753 : };
754 :
755 : /*
756 : * Represents an atomized string which does not contain an index (that is, an
757 : * unsigned 32-bit value). Thus for any PropertyName propname,
758 : * ToString(ToUint32(propname)) never equals propname.
759 : *
760 : * To more concretely illustrate the utility of PropertyName, consider that it
761 : * is used to partition, in a type-safe manner, the ways to refer to a
762 : * property, as follows:
763 : *
764 : * - uint32_t indexes,
765 : * - PropertyName strings which don't encode uint32_t indexes, and
766 : * - jsspecial special properties (non-ES5 properties like object-valued
767 : * jsids, JSID_EMPTY, JSID_VOID, E4X's default XML namespace, and maybe in
768 : * the future Harmony-proposed private names).
769 : */
770 : class PropertyName : public JSAtom
771 : {};
772 :
773 : JS_STATIC_ASSERT(sizeof(PropertyName) == sizeof(JSString));
774 :
775 : } /* namespace js */
776 :
777 : /* Avoid requiring vm/String-inl.h just to call getChars. */
778 :
779 : JS_ALWAYS_INLINE const jschar *
780 28964775 : JSString::getChars(JSContext *cx)
781 : {
782 28964775 : if (JSLinearString *str = ensureLinear(cx))
783 28964775 : return str->chars();
784 0 : return NULL;
785 : }
786 :
787 : JS_ALWAYS_INLINE const jschar *
788 43628538 : JSString::getCharsZ(JSContext *cx)
789 : {
790 43628538 : if (JSFlatString *str = ensureFlat(cx))
791 43628538 : return str->chars();
792 0 : return NULL;
793 : }
794 :
795 : JS_ALWAYS_INLINE JSLinearString *
796 41754069 : JSString::ensureLinear(JSContext *cx)
797 : {
798 41754069 : return isLinear()
799 : ? &asLinear()
800 41754069 : : asRope().flatten(cx);
801 : }
802 :
803 : JS_ALWAYS_INLINE JSFlatString *
804 85731760 : JSString::ensureFlat(JSContext *cx)
805 : {
806 85731760 : return isFlat()
807 : ? &asFlat()
808 154117 : : isDependent()
809 26772 : ? asDependent().undepend(cx)
810 85912649 : : asRope().flatten(cx);
811 : }
812 :
813 : JS_ALWAYS_INLINE JSFixedString *
814 544236 : JSString::ensureFixed(JSContext *cx)
815 : {
816 544236 : if (!ensureFlat(cx))
817 0 : return NULL;
818 544236 : if (isExtensible()) {
819 654 : JS_ASSERT((d.lengthAndFlags & FLAT_MASK) == 0);
820 : JS_STATIC_ASSERT(EXTENSIBLE_FLAGS == (JS_BIT(2) | JS_BIT(3)));
821 : JS_STATIC_ASSERT(FIXED_FLAGS == JS_BIT(2));
822 654 : d.lengthAndFlags ^= JS_BIT(3);
823 : }
824 544236 : return &asFixed();
825 : }
826 :
827 : inline js::PropertyName *
828 91051632 : JSAtom::asPropertyName()
829 : {
830 : #ifdef DEBUG
831 : uint32_t dummy;
832 91051632 : JS_ASSERT(!isIndex(&dummy));
833 : #endif
834 91051632 : return static_cast<js::PropertyName *>(this);
835 : }
836 :
837 : #endif
|