summaryrefslogtreecommitdiffstats
path: root/js/src/vm/String.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/String.h')
-rw-r--r--js/src/vm/String.h1509
1 files changed, 1509 insertions, 0 deletions
diff --git a/js/src/vm/String.h b/js/src/vm/String.h
new file mode 100644
index 000000000..1a0c58575
--- /dev/null
+++ b/js/src/vm/String.h
@@ -0,0 +1,1509 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_String_h
+#define vm_String_h
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Range.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jsstr.h"
+
+#include "gc/Barrier.h"
+#include "gc/Heap.h"
+#include "gc/Marking.h"
+#include "gc/Rooting.h"
+#include "js/CharacterEncoding.h"
+#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
+
+class JSDependentString;
+class JSExtensibleString;
+class JSExternalString;
+class JSInlineString;
+class JSRope;
+
+namespace js {
+
+class AutoStableStringChars;
+class StaticStrings;
+class PropertyName;
+
+/* The buffer length required to contain any unsigned 32-bit integer. */
+static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
+
+} /* namespace js */
+
+/*
+ * JavaScript strings
+ *
+ * Conceptually, a JS string is just an array of chars and a length. This array
+ * of chars may or may not be null-terminated and, if it is, the null character
+ * is not included in the length.
+ *
+ * To improve performance of common operations, the following optimizations are
+ * made which affect the engine's representation of strings:
+ *
+ * - The plain vanilla representation is a "flat" string which consists of a
+ * string header in the GC heap and a malloc'd null terminated char array.
+ *
+ * - To avoid copying a substring of an existing "base" string , a "dependent"
+ * string (JSDependentString) can be created which points into the base
+ * string's char array.
+ *
+ * - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created
+ * to represent a delayed string concatenation. Concatenation (called
+ * flattening) is performed if and when a linear char array is requested. In
+ * general, ropes form a binary dag whose internal nodes are JSRope string
+ * headers with no associated char array and whose leaf nodes are either flat
+ * or dependent strings.
+ *
+ * - To avoid copying the leftmost string when flattening, we may produce an
+ * "extensible" string, which tracks not only its actual length but also its
+ * buffer's overall size. If such an "extensible" string appears as the
+ * leftmost string in a subsequent flatten, and its buffer has enough unused
+ * space, we can simply flatten the rest of the ropes into its buffer,
+ * leaving its text in place. We then transfer ownership of its buffer to the
+ * flattened rope, and mutate the donor extensible string into a dependent
+ * string referencing its original buffer.
+ *
+ * (The term "extensible" does not imply that we ever 'realloc' the buffer.
+ * Extensible strings may have dependent strings pointing into them, and the
+ * JSAPI hands out pointers to flat strings' buffers, so resizing with
+ * 'realloc' is generally not possible.)
+ *
+ * - To avoid allocating small char arrays, short strings can be stored inline
+ * in the string header (JSInlineString). These come in two flavours:
+ * JSThinInlineString, which is the same size as JSString; and
+ * JSFatInlineString, which has a larger header and so can fit more chars.
+ *
+ * - To avoid comparing O(n) string equality comparison, strings can be
+ * canonicalized to "atoms" (JSAtom) such that there is a single atom with a
+ * given (length,chars).
+ *
+ * - To avoid copying all strings created through the JSAPI, an "external"
+ * string (JSExternalString) can be created whose chars are managed by the
+ * JSAPI client.
+ *
+ * - To avoid using two bytes per character for every string, string characters
+ * are stored as Latin1 instead of TwoByte if all characters are representable
+ * in Latin1.
+ *
+ * Although all strings share the same basic memory layout, we can conceptually
+ * arrange them into a hierarchy of operations/invariants and represent this
+ * hierarchy in C++ with classes:
+ *
+ * C++ type operations+fields / invariants+properties
+ * ========================== =========================================
+ * JSString (abstract) get(Latin1|TwoByte)CharsZ, get(Latin1|TwoByte)Chars, length / -
+ * | \
+ * | JSRope leftChild, rightChild / -
+ * |
+ * JSLinearString (abstract) latin1Chars, twoByteChars / might be null-terminated
+ * | |
+ * | +-- JSDependentString base / -
+ * | |
+ * | +-- JSExternalString - / char array memory managed by embedding
+ * |
+ * JSFlatString - / null terminated
+ * | |
+ * | +-- JSExtensibleString tracks total buffer capacity (including current text)
+ * | |
+ * | +-- JSUndependedString original dependent base / -
+ * | |
+ * | +-- JSInlineString (abstract) - / chars stored in header
+ * | |
+ * | +-- JSThinInlineString - / header is normal
+ * | |
+ * | +-- JSFatInlineString - / header is fat
+ * |
+ * JSAtom (abstract) - / string equality === pointer equality
+ * | |
+ * | +-- js::NormalAtom - JSFlatString + atom hash code
+ * | |
+ * | +-- js::FatInlineAtom - JSFatInlineString + atom hash code
+ * |
+ * js::PropertyName - / chars don't contain an index (uint32_t)
+ *
+ * Classes marked with (abstract) above are not literally C++ Abstract Base
+ * Classes (since there are no virtual functions, pure or not, in this
+ * hierarchy), but have the same meaning: there are no strings with this type as
+ * its most-derived type.
+ *
+ * Atoms can additionally be permanent, i.e. unable to be collected, and can
+ * be combined with other string types to create additional most-derived types
+ * that satisfy the invariants of more than one of the abovementioned
+ * most-derived types. Furthermore, each atom stores a hash number (based on its
+ * chars). This hash number is used as key in the atoms table and when the atom
+ * is used as key in a JS Map/Set.
+ *
+ * Derived string types can be queried from ancestor types via isX() and
+ * retrieved with asX() debug-only-checked casts.
+ *
+ * The ensureX() operations mutate 'this' in place to effectively the type to be
+ * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
+ */
+
+class JSString : public js::gc::TenuredCell
+{
+ protected:
+ static const size_t NUM_INLINE_CHARS_LATIN1 = 2 * sizeof(void*) / sizeof(JS::Latin1Char);
+ static const size_t NUM_INLINE_CHARS_TWO_BYTE = 2 * sizeof(void*) / sizeof(char16_t);
+
+ /* Fields only apply to string types commented on the right. */
+ struct Data
+ {
+ union {
+ struct {
+ uint32_t flags; /* JSString */
+ uint32_t length; /* JSString */
+ };
+ uintptr_t flattenData; /* JSRope (temporary while flattening) */
+ } u1;
+ union {
+ union {
+ /* JS(Fat)InlineString */
+ JS::Latin1Char inlineStorageLatin1[NUM_INLINE_CHARS_LATIN1];
+ char16_t inlineStorageTwoByte[NUM_INLINE_CHARS_TWO_BYTE];
+ };
+ struct {
+ union {
+ const JS::Latin1Char* nonInlineCharsLatin1; /* JSLinearString, except JS(Fat)InlineString */
+ const char16_t* nonInlineCharsTwoByte;/* JSLinearString, except JS(Fat)InlineString */
+ JSString* left; /* JSRope */
+ } u2;
+ union {
+ JSLinearString* base; /* JS(Dependent|Undepended)String */
+ JSString* right; /* JSRope */
+ size_t capacity; /* JSFlatString (extensible) */
+ const JSStringFinalizer* externalFinalizer;/* JSExternalString */
+ } u3;
+ } s;
+ };
+ } d;
+
+ public:
+ /* Flags exposed only for jits */
+
+ /*
+ * The Flags Word
+ *
+ * The flags word stores both the string's type and its character encoding.
+ *
+ * If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1
+ * instead of TwoByte. This flag can also be set for ropes, if both the
+ * left and right nodes are Latin1. Flattening will result in a Latin1
+ * string in this case.
+ *
+ * The other flags store the string's type. Instead of using a dense index
+ * to represent the most-derived type, string types are encoded to allow
+ * single-op tests for hot queries (isRope, isDependent, isFlat, isAtom)
+ * which, in view of subtyping, would require slower
+ * (isX() || isY() || isZ()).
+ *
+ * The string type encoding can be summarized as follows. The "instance
+ * encoding" entry for a type specifies the flag bits used to create a
+ * string instance of that type. Abstract types have no instances and thus
+ * have no such entry. The "subtype predicate" entry for a type specifies
+ * the predicate used to query whether a JSString instance is subtype
+ * (reflexively) of that type.
+ *
+ * String Instance Subtype
+ * type encoding predicate
+ * ------------------------------------
+ * Rope 000000 000000
+ * Linear - !000000
+ * HasBase - xxxx1x
+ * Dependent 000010 000010
+ * External 100000 100000
+ * Flat - xxxxx1
+ * Undepended 000011 000011
+ * Extensible 010001 010001
+ * Inline 000101 xxx1xx
+ * FatInline 010101 x1x1xx
+ * Atom 001001 xx1xxx
+ * PermanentAtom 101001 1x1xxx
+ * InlineAtom - xx11xx
+ * FatInlineAtom - x111xx
+ *
+ * Note that the first 4 flag bits (from right to left in the previous table)
+ * have the following meaning and can be used for some hot queries:
+ *
+ * Bit 0: IsFlat
+ * Bit 1: HasBase (Dependent, Undepended)
+ * Bit 2: IsInline (Inline, FatInline)
+ * Bit 3: IsAtom (Atom, PermanentAtom)
+ *
+ * "HasBase" here refers to the two string types that have a 'base' field:
+ * JSDependentString and JSUndependedString.
+ * A JSUndependedString is a JSDependentString which has been 'fixed' (by ensureFixed)
+ * to be null-terminated. In such cases, the string must keep marking its base since
+ * there may be any number of *other* JSDependentStrings transitively depending on it.
+ *
+ */
+
+ static const uint32_t FLAT_BIT = JS_BIT(0);
+ static const uint32_t HAS_BASE_BIT = JS_BIT(1);
+ static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
+ static const uint32_t ATOM_BIT = JS_BIT(3);
+
+ static const uint32_t ROPE_FLAGS = 0;
+ static const uint32_t DEPENDENT_FLAGS = HAS_BASE_BIT;
+ static const uint32_t UNDEPENDED_FLAGS = FLAT_BIT | HAS_BASE_BIT;
+ static const uint32_t EXTENSIBLE_FLAGS = FLAT_BIT | JS_BIT(4);
+ static const uint32_t EXTERNAL_FLAGS = JS_BIT(5);
+
+ static const uint32_t FAT_INLINE_MASK = INLINE_CHARS_BIT | JS_BIT(4);
+ static const uint32_t PERMANENT_ATOM_MASK = ATOM_BIT | JS_BIT(5);
+
+ /* Initial flags for thin inline and fat inline strings. */
+ static const uint32_t INIT_THIN_INLINE_FLAGS = FLAT_BIT | INLINE_CHARS_BIT;
+ static const uint32_t INIT_FAT_INLINE_FLAGS = FLAT_BIT | FAT_INLINE_MASK;
+
+ static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
+
+ static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
+
+ static const uint32_t MAX_LENGTH = js::MaxStringLength;
+
+ static const JS::Latin1Char MAX_LATIN1_CHAR = 0xff;
+
+ /*
+ * Helper function to validate that a string of a given length is
+ * representable by a JSString. An allocation overflow is reported if false
+ * is returned.
+ */
+ static inline bool validateLength(js::ExclusiveContext* maybecx, size_t length);
+
+ static void staticAsserts() {
+ static_assert(JSString::MAX_LENGTH < UINT32_MAX, "Length must fit in 32 bits");
+ static_assert(sizeof(JSString) ==
+ (offsetof(JSString, d.inlineStorageLatin1) +
+ NUM_INLINE_CHARS_LATIN1 * sizeof(char)),
+ "Inline Latin1 chars must fit in a JSString");
+ static_assert(sizeof(JSString) ==
+ (offsetof(JSString, d.inlineStorageTwoByte) +
+ NUM_INLINE_CHARS_TWO_BYTE * sizeof(char16_t)),
+ "Inline char16_t chars must fit in a JSString");
+
+ /* Ensure js::shadow::String has the same layout. */
+ using js::shadow::String;
+ static_assert(offsetof(JSString, d.u1.length) == offsetof(String, length),
+ "shadow::String length offset must match JSString");
+ static_assert(offsetof(JSString, d.u1.flags) == offsetof(String, flags),
+ "shadow::String flags offset must match JSString");
+ static_assert(offsetof(JSString, d.s.u2.nonInlineCharsLatin1) == offsetof(String, nonInlineCharsLatin1),
+ "shadow::String nonInlineChars offset must match JSString");
+ static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) == offsetof(String, nonInlineCharsTwoByte),
+ "shadow::String nonInlineChars offset must match JSString");
+ static_assert(offsetof(JSString, d.inlineStorageLatin1) == offsetof(String, inlineStorageLatin1),
+ "shadow::String inlineStorage offset must match JSString");
+ static_assert(offsetof(JSString, d.inlineStorageTwoByte) == offsetof(String, inlineStorageTwoByte),
+ "shadow::String inlineStorage offset must match JSString");
+ static_assert(INLINE_CHARS_BIT == String::INLINE_CHARS_BIT,
+ "shadow::String::INLINE_CHARS_BIT must match JSString::INLINE_CHARS_BIT");
+ static_assert(LATIN1_CHARS_BIT == String::LATIN1_CHARS_BIT,
+ "shadow::String::LATIN1_CHARS_BIT must match JSString::LATIN1_CHARS_BIT");
+ static_assert(TYPE_FLAGS_MASK == String::TYPE_FLAGS_MASK,
+ "shadow::String::TYPE_FLAGS_MASK must match JSString::TYPE_FLAGS_MASK");
+ static_assert(ROPE_FLAGS == String::ROPE_FLAGS,
+ "shadow::String::ROPE_FLAGS must match JSString::ROPE_FLAGS");
+ }
+
+ /* Avoid lame compile errors in JSRope::flatten */
+ friend class JSRope;
+
+ friend class js::gc::RelocationOverlay;
+
+ protected:
+ template <typename CharT>
+ MOZ_ALWAYS_INLINE
+ void setNonInlineChars(const CharT* chars);
+
+ public:
+ /* All strings have length. */
+
+ MOZ_ALWAYS_INLINE
+ size_t length() const {
+ return d.u1.length;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool empty() const {
+ return d.u1.length == 0;
+ }
+
+ inline bool getChar(js::ExclusiveContext* cx, size_t index, char16_t* code);
+
+ /* Strings have either Latin1 or TwoByte chars. */
+ bool hasLatin1Chars() const {
+ return d.u1.flags & LATIN1_CHARS_BIT;
+ }
+ bool hasTwoByteChars() const {
+ return !(d.u1.flags & LATIN1_CHARS_BIT);
+ }
+
+ /* Fallible conversions to more-derived string types. */
+
+ inline JSLinearString* ensureLinear(js::ExclusiveContext* cx);
+ JSFlatString* ensureFlat(JSContext* cx);
+
+ static bool ensureLinear(js::ExclusiveContext* cx, JSString* str) {
+ return str->ensureLinear(cx) != nullptr;
+ }
+
+ /* Type query and debug-checked casts */
+
+ MOZ_ALWAYS_INLINE
+ bool isRope() const {
+ return (d.u1.flags & TYPE_FLAGS_MASK) == ROPE_FLAGS;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSRope& asRope() const {
+ MOZ_ASSERT(isRope());
+ return *(JSRope*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isLinear() const {
+ return !isRope();
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSLinearString& asLinear() const {
+ MOZ_ASSERT(JSString::isLinear());
+ return *(JSLinearString*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isDependent() const {
+ return (d.u1.flags & TYPE_FLAGS_MASK) == DEPENDENT_FLAGS;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSDependentString& asDependent() const {
+ MOZ_ASSERT(isDependent());
+ return *(JSDependentString*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isFlat() const {
+ return d.u1.flags & FLAT_BIT;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSFlatString& asFlat() const {
+ MOZ_ASSERT(isFlat());
+ return *(JSFlatString*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isExtensible() const {
+ return (d.u1.flags & TYPE_FLAGS_MASK) == EXTENSIBLE_FLAGS;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSExtensibleString& asExtensible() const {
+ MOZ_ASSERT(isExtensible());
+ return *(JSExtensibleString*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isInline() const {
+ return d.u1.flags & INLINE_CHARS_BIT;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSInlineString& asInline() const {
+ MOZ_ASSERT(isInline());
+ return *(JSInlineString*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isFatInline() const {
+ return (d.u1.flags & FAT_INLINE_MASK) == FAT_INLINE_MASK;
+ }
+
+ /* For hot code, prefer other type queries. */
+ bool isExternal() const {
+ return (d.u1.flags & TYPE_FLAGS_MASK) == EXTERNAL_FLAGS;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSExternalString& asExternal() const {
+ MOZ_ASSERT(isExternal());
+ return *(JSExternalString*)this;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isUndepended() const {
+ return (d.u1.flags & TYPE_FLAGS_MASK) == UNDEPENDED_FLAGS;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isAtom() const {
+ return d.u1.flags & ATOM_BIT;
+ }
+
+ MOZ_ALWAYS_INLINE
+ bool isPermanentAtom() const {
+ return (d.u1.flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_MASK;
+ }
+
+ MOZ_ALWAYS_INLINE
+ JSAtom& asAtom() const {
+ MOZ_ASSERT(isAtom());
+ return *(JSAtom*)this;
+ }
+
+ /* Only called by the GC for dependent or undepended strings. */
+
+ inline bool hasBase() const {
+ return d.u1.flags & HAS_BASE_BIT;
+ }
+
+ inline JSLinearString* base() const;
+
+ void traceBase(JSTracer* trc);
+
+ /* Only called by the GC for strings with the AllocKind::STRING kind. */
+
+ inline void finalize(js::FreeOp* fop);
+
+ /* Gets the number of bytes that the chars take on the heap. */
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ /* Offsets for direct field from jit code. */
+
+ static size_t offsetOfLength() {
+ return offsetof(JSString, d.u1.length);
+ }
+ static size_t offsetOfFlags() {
+ return offsetof(JSString, d.u1.flags);
+ }
+
+ static size_t offsetOfNonInlineChars() {
+ static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) ==
+ offsetof(JSString, d.s.u2.nonInlineCharsLatin1),
+ "nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
+ return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte);
+ }
+
+ static const JS::TraceKind TraceKind = JS::TraceKind::String;
+
+#ifdef DEBUG
+ void dump(FILE* fp);
+ void dumpCharsNoNewline(FILE* fp);
+ void dump();
+ void dumpCharsNoNewline();
+ void dumpRepresentation(FILE* fp, int indent) const;
+ void dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const;
+
+ template <typename CharT>
+ static void dumpChars(const CharT* s, size_t len, FILE* fp=stderr);
+
+ bool equals(const char* s);
+#endif
+
+ void traceChildren(JSTracer* trc);
+
+ static MOZ_ALWAYS_INLINE void readBarrier(JSString* thing) {
+ if (thing->isPermanentAtom())
+ return;
+
+ TenuredCell::readBarrier(thing);
+ }
+
+ static MOZ_ALWAYS_INLINE void writeBarrierPre(JSString* thing) {
+ if (isNullLike(thing) || thing->isPermanentAtom())
+ return;
+
+ TenuredCell::writeBarrierPre(thing);
+ }
+
+ private:
+ JSString() = delete;
+ JSString(const JSString& other) = delete;
+ void operator=(const JSString& other) = delete;
+};
+
+class JSRope : public JSString
+{
+ template <typename CharT>
+ bool copyCharsInternal(js::ExclusiveContext* cx, js::ScopedJSFreePtr<CharT>& out,
+ bool nullTerminate) const;
+
+ enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
+
+ template<UsingBarrier b, typename CharT>
+ JSFlatString* flattenInternal(js::ExclusiveContext* cx);
+
+ template<UsingBarrier b>
+ JSFlatString* flattenInternal(js::ExclusiveContext* cx);
+
+ friend class JSString;
+ JSFlatString* flatten(js::ExclusiveContext* cx);
+
+ void init(js::ExclusiveContext* cx, JSString* left, JSString* right, size_t length);
+
+ public:
+ template <js::AllowGC allowGC>
+ static inline JSRope* new_(js::ExclusiveContext* cx,
+ typename js::MaybeRooted<JSString*, allowGC>::HandleType left,
+ typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
+ size_t length);
+
+ bool copyLatin1Chars(js::ExclusiveContext* cx,
+ js::ScopedJSFreePtr<JS::Latin1Char>& out) const;
+ bool copyTwoByteChars(js::ExclusiveContext* cx, js::ScopedJSFreePtr<char16_t>& out) const;
+
+ bool copyLatin1CharsZ(js::ExclusiveContext* cx,
+ js::ScopedJSFreePtr<JS::Latin1Char>& out) const;
+ bool copyTwoByteCharsZ(js::ExclusiveContext* cx, js::ScopedJSFreePtr<char16_t>& out) const;
+
+ template <typename CharT>
+ bool copyChars(js::ExclusiveContext* cx, js::ScopedJSFreePtr<CharT>& out) const;
+
+ JSString* leftChild() const {
+ MOZ_ASSERT(isRope());
+ return d.s.u2.left;
+ }
+
+ JSString* rightChild() const {
+ MOZ_ASSERT(isRope());
+ return d.s.u3.right;
+ }
+
+ void traceChildren(JSTracer* trc);
+
+ static size_t offsetOfLeft() {
+ return offsetof(JSRope, d.s.u2.left);
+ }
+ static size_t offsetOfRight() {
+ return offsetof(JSRope, d.s.u3.right);
+ }
+
+#ifdef DEBUG
+ void dumpRepresentation(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSRope) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSLinearString : public JSString
+{
+ friend class JSString;
+ friend class js::AutoStableStringChars;
+
+ /* Vacuous and therefore unimplemented. */
+ JSLinearString* ensureLinear(js::ExclusiveContext* cx) = delete;
+ bool isLinear() const = delete;
+ JSLinearString& asLinear() const = delete;
+
+ protected:
+ /* Returns void pointer to latin1/twoByte chars, for finalizers. */
+ MOZ_ALWAYS_INLINE
+ void* nonInlineCharsRaw() const {
+ MOZ_ASSERT(!isInline());
+ static_assert(offsetof(JSLinearString, d.s.u2.nonInlineCharsTwoByte) ==
+ offsetof(JSLinearString, d.s.u2.nonInlineCharsLatin1),
+ "nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
+ return (void*)d.s.u2.nonInlineCharsTwoByte;
+ }
+
+ MOZ_ALWAYS_INLINE const JS::Latin1Char* rawLatin1Chars() const;
+ MOZ_ALWAYS_INLINE const char16_t* rawTwoByteChars() const;
+
+ public:
+ template<typename CharT>
+ MOZ_ALWAYS_INLINE
+ const CharT* nonInlineChars(const JS::AutoCheckCannotGC& nogc) const;
+
+ MOZ_ALWAYS_INLINE
+ const JS::Latin1Char* nonInlineLatin1Chars(const JS::AutoCheckCannotGC& nogc) const {
+ MOZ_ASSERT(!isInline());
+ MOZ_ASSERT(hasLatin1Chars());
+ return d.s.u2.nonInlineCharsLatin1;
+ }
+
+ MOZ_ALWAYS_INLINE
+ const char16_t* nonInlineTwoByteChars(const JS::AutoCheckCannotGC& nogc) const {
+ MOZ_ASSERT(!isInline());
+ MOZ_ASSERT(hasTwoByteChars());
+ return d.s.u2.nonInlineCharsTwoByte;
+ }
+
+ template<typename CharT>
+ MOZ_ALWAYS_INLINE
+ const CharT* chars(const JS::AutoCheckCannotGC& nogc) const;
+
+ MOZ_ALWAYS_INLINE
+ const JS::Latin1Char* latin1Chars(const JS::AutoCheckCannotGC& nogc) const {
+ return rawLatin1Chars();
+ }
+
+ MOZ_ALWAYS_INLINE
+ const char16_t* twoByteChars(const JS::AutoCheckCannotGC& nogc) const {
+ return rawTwoByteChars();
+ }
+
+ mozilla::Range<const JS::Latin1Char> latin1Range(const JS::AutoCheckCannotGC& nogc) const {
+ MOZ_ASSERT(JSString::isLinear());
+ return mozilla::Range<const JS::Latin1Char>(latin1Chars(nogc), length());
+ }
+
+ mozilla::Range<const char16_t> twoByteRange(const JS::AutoCheckCannotGC& nogc) const {
+ MOZ_ASSERT(JSString::isLinear());
+ return mozilla::Range<const char16_t>(twoByteChars(nogc), length());
+ }
+
+ MOZ_ALWAYS_INLINE
+ char16_t latin1OrTwoByteChar(size_t index) const {
+ MOZ_ASSERT(JSString::isLinear());
+ MOZ_ASSERT(index < length());
+ JS::AutoCheckCannotGC nogc;
+ return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index];
+ }
+
+#ifdef DEBUG
+ void dumpRepresentationChars(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSLinearString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSDependentString : public JSLinearString
+{
+ friend class JSString;
+ JSFlatString* undepend(JSContext* cx);
+
+ template <typename CharT>
+ JSFlatString* undependInternal(JSContext* cx);
+
+ void init(js::ExclusiveContext* cx, JSLinearString* base, size_t start,
+ size_t length);
+
+ /* Vacuous and therefore unimplemented. */
+ bool isDependent() const = delete;
+ JSDependentString& asDependent() const = delete;
+
+ /* The offset of this string's chars in base->chars(). */
+ MOZ_ALWAYS_INLINE mozilla::Maybe<size_t> baseOffset() const {
+ MOZ_ASSERT(JSString::isDependent());
+ JS::AutoCheckCannotGC nogc;
+ if (MOZ_UNLIKELY(base()->isUndepended()))
+ return mozilla::Nothing();
+ size_t offset;
+ if (hasTwoByteChars())
+ offset = twoByteChars(nogc) - base()->twoByteChars(nogc);
+ else
+ offset = latin1Chars(nogc) - base()->latin1Chars(nogc);
+ MOZ_ASSERT(offset < base()->length());
+ return mozilla::Some(offset);
+ }
+
+ public:
+ static inline JSLinearString* new_(js::ExclusiveContext* cx, JSLinearString* base,
+ size_t start, size_t length);
+
+ inline static size_t offsetOfBase() {
+ return offsetof(JSDependentString, d.s.u3.base);
+ }
+
+#ifdef DEBUG
+ void dumpRepresentation(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSDependentString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSFlatString : public JSLinearString
+{
+ /* Vacuous and therefore unimplemented. */
+ JSFlatString* ensureFlat(JSContext* cx) = delete;
+ bool isFlat() const = delete;
+ JSFlatString& asFlat() const = delete;
+
+ template <typename CharT>
+ static bool isIndexSlow(const CharT* s, size_t length, uint32_t* indexp);
+
+ void init(const char16_t* chars, size_t length);
+ void init(const JS::Latin1Char* chars, size_t length);
+
+ public:
+ template <js::AllowGC allowGC, typename CharT>
+ static inline JSFlatString* new_(js::ExclusiveContext* cx,
+ const CharT* chars, size_t length);
+
+ /*
+ * Returns true if this string's characters store an unsigned 32-bit
+ * integer value, initializing *indexp to that value if so. (Thus if
+ * calling isIndex returns true, js::IndexToString(cx, *indexp) will be a
+ * string equal to this string.)
+ */
+ inline bool isIndex(uint32_t* indexp) const {
+ MOZ_ASSERT(JSString::isFlat());
+ JS::AutoCheckCannotGC nogc;
+ if (hasLatin1Chars()) {
+ const JS::Latin1Char* s = latin1Chars(nogc);
+ return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+ }
+ const char16_t* s = twoByteChars(nogc);
+ return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+ }
+
+ /*
+ * Returns a property name represented by this string, or null on failure.
+ * You must verify that this is not an index per isIndex before calling
+ * this method.
+ */
+ inline js::PropertyName* toPropertyName(JSContext* cx);
+
+ /*
+ * Once a JSFlatString sub-class has been added to the atom state, this
+ * operation changes the string to the JSAtom type, in place.
+ */
+ MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoAtom(js::HashNumber hash);
+ MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoPermanentAtom(js::HashNumber hash);
+
+ inline void finalize(js::FreeOp* fop);
+
+#ifdef DEBUG
+ void dumpRepresentation(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSFlatString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSExtensibleString : public JSFlatString
+{
+ /* Vacuous and therefore unimplemented. */
+ bool isExtensible() const = delete;
+ JSExtensibleString& asExtensible() const = delete;
+
+ public:
+ MOZ_ALWAYS_INLINE
+ size_t capacity() const {
+ MOZ_ASSERT(JSString::isExtensible());
+ return d.s.u3.capacity;
+ }
+
+#ifdef DEBUG
+ void dumpRepresentation(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSExtensibleString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSInlineString : public JSFlatString
+{
+ public:
+ MOZ_ALWAYS_INLINE
+ const JS::Latin1Char* latin1Chars(const JS::AutoCheckCannotGC& nogc) const {
+ MOZ_ASSERT(JSString::isInline());
+ MOZ_ASSERT(hasLatin1Chars());
+ return d.inlineStorageLatin1;
+ }
+
+ MOZ_ALWAYS_INLINE
+ const char16_t* twoByteChars(const JS::AutoCheckCannotGC& nogc) const {
+ MOZ_ASSERT(JSString::isInline());
+ MOZ_ASSERT(hasTwoByteChars());
+ return d.inlineStorageTwoByte;
+ }
+
+ template<typename CharT>
+ static bool lengthFits(size_t length);
+
+ static size_t offsetOfInlineStorage() {
+ return offsetof(JSInlineString, d.inlineStorageTwoByte);
+ }
+
+#ifdef DEBUG
+ void dumpRepresentation(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSInlineString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+/*
+ * On 32-bit platforms, JSThinInlineString can store 7 Latin1 characters or 3
+ * TwoByte characters (excluding null terminator) inline. On 64-bit platforms,
+ * these numbers are 15 and 7, respectively.
+ */
+class JSThinInlineString : public JSInlineString
+{
+ public:
+ static const size_t MAX_LENGTH_LATIN1 = NUM_INLINE_CHARS_LATIN1 - 1;
+ static const size_t MAX_LENGTH_TWO_BYTE = NUM_INLINE_CHARS_TWO_BYTE - 1;
+
+ template <js::AllowGC allowGC>
+ static inline JSThinInlineString* new_(js::ExclusiveContext* cx);
+
+ template <typename CharT>
+ inline CharT* init(size_t length);
+
+ template<typename CharT>
+ static bool lengthFits(size_t length);
+};
+
+static_assert(sizeof(JSThinInlineString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+/*
+ * On both 32-bit and 64-bit platforms, MAX_LENGTH_TWO_BYTE is 11 and
+ * MAX_LENGTH_LATIN1 is 23 (excluding null terminator). This is deliberate,
+ * in order to minimize potential performance differences between 32-bit and
+ * 64-bit platforms.
+ *
+ * There are still some differences due to NUM_INLINE_CHARS_* being different.
+ * E.g. TwoByte strings of length 4--7 will be JSFatInlineStrings on 32-bit
+ * platforms and JSThinInlineStrings on 64-bit platforms. But the more
+ * significant transition from inline strings to non-inline strings occurs at
+ * length 11 (for TwoByte strings) and 23 (Latin1 strings) on both 32-bit and
+ * 64-bit platforms.
+ */
+class JSFatInlineString : public JSInlineString
+{
+ static const size_t INLINE_EXTENSION_CHARS_LATIN1 = 24 - NUM_INLINE_CHARS_LATIN1;
+ static const size_t INLINE_EXTENSION_CHARS_TWO_BYTE = 12 - NUM_INLINE_CHARS_TWO_BYTE;
+
+ protected: /* to fool clang into not warning this is unused */
+ union {
+ char inlineStorageExtensionLatin1[INLINE_EXTENSION_CHARS_LATIN1];
+ char16_t inlineStorageExtensionTwoByte[INLINE_EXTENSION_CHARS_TWO_BYTE];
+ };
+
+ public:
+ template <js::AllowGC allowGC>
+ static inline JSFatInlineString* new_(js::ExclusiveContext* cx);
+
+ static const size_t MAX_LENGTH_LATIN1 = JSString::NUM_INLINE_CHARS_LATIN1 +
+ INLINE_EXTENSION_CHARS_LATIN1
+ -1 /* null terminator */;
+
+ static const size_t MAX_LENGTH_TWO_BYTE = JSString::NUM_INLINE_CHARS_TWO_BYTE +
+ INLINE_EXTENSION_CHARS_TWO_BYTE
+ -1 /* null terminator */;
+
+ template <typename CharT>
+ inline CharT* init(size_t length);
+
+ template<typename CharT>
+ static bool lengthFits(size_t length);
+
+ /* Only called by the GC for strings with the AllocKind::FAT_INLINE_STRING kind. */
+
+ MOZ_ALWAYS_INLINE void finalize(js::FreeOp* fop);
+};
+
+static_assert(sizeof(JSFatInlineString) % js::gc::CellSize == 0,
+ "fat inline strings shouldn't waste space up to the next cell "
+ "boundary");
+
+class JSExternalString : public JSLinearString
+{
+ void init(const char16_t* chars, size_t length, const JSStringFinalizer* fin);
+
+ /* Vacuous and therefore unimplemented. */
+ bool isExternal() const = delete;
+ JSExternalString& asExternal() const = delete;
+
+ public:
+ static inline JSExternalString* new_(JSContext* cx, const char16_t* chars, size_t length,
+ const JSStringFinalizer* fin);
+
+ const JSStringFinalizer* externalFinalizer() const {
+ MOZ_ASSERT(JSString::isExternal());
+ return d.s.u3.externalFinalizer;
+ }
+
+ /*
+ * External chars are never allocated inline or in the nursery, so we can
+ * safely expose this without requiring an AutoCheckCannotGC argument.
+ */
+ const char16_t* twoByteChars() const {
+ return rawTwoByteChars();
+ }
+
+ /* Only called by the GC for strings with the AllocKind::EXTERNAL_STRING kind. */
+
+ inline void finalize(js::FreeOp* fop);
+
+ JSFlatString* ensureFlat(JSContext* cx);
+
+#ifdef DEBUG
+ void dumpRepresentation(FILE* fp, int indent) const;
+#endif
+};
+
+static_assert(sizeof(JSExternalString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSUndependedString : public JSFlatString
+{
+ /*
+ * JSUndependedString is not explicitly used and is only present for
+ * consistency. See JSDependentString::undepend for how a JSDependentString
+ * gets morphed into a JSUndependedString.
+ */
+};
+
+static_assert(sizeof(JSUndependedString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+class JSAtom : public JSFlatString
+{
+ /* Vacuous and therefore unimplemented. */
+ bool isAtom() const = delete;
+ JSAtom& asAtom() const = delete;
+
+ public:
+ /* Returns the PropertyName for this. isIndex() must be false. */
+ inline js::PropertyName* asPropertyName();
+
+ inline void finalize(js::FreeOp* fop);
+
+ MOZ_ALWAYS_INLINE
+ bool isPermanent() const {
+ return JSString::isPermanentAtom();
+ }
+
+ // Transform this atom into a permanent atom. This is only done during
+ // initialization of the runtime.
+ MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
+ d.u1.flags |= PERMANENT_ATOM_MASK;
+ }
+
+ inline js::HashNumber hash() const;
+ inline void initHash(js::HashNumber hash);
+
+#ifdef DEBUG
+ void dump(FILE* fp);
+ void dump();
+#endif
+};
+
+static_assert(sizeof(JSAtom) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+namespace js {
+
+class NormalAtom : public JSAtom
+{
+ protected: // Silence Clang unused-field warning.
+ HashNumber hash_;
+ uint32_t padding_; // Ensure the size is a multiple of gc::CellSize.
+
+ public:
+ HashNumber hash() const {
+ return hash_;
+ }
+ void initHash(HashNumber hash) {
+ hash_ = hash;
+ }
+};
+
+static_assert(sizeof(NormalAtom) == sizeof(JSString) + sizeof(uint64_t),
+ "NormalAtom must have size of a string + HashNumber, "
+ "aligned to gc::CellSize");
+
+class FatInlineAtom : public JSAtom
+{
+ protected: // Silence Clang unused-field warning.
+ char inlineStorage_[sizeof(JSFatInlineString) - sizeof(JSString)];
+ HashNumber hash_;
+ uint32_t padding_; // Ensure the size is a multiple of gc::CellSize.
+
+ public:
+ HashNumber hash() const {
+ return hash_;
+ }
+ void initHash(HashNumber hash) {
+ hash_ = hash;
+ }
+};
+
+static_assert(sizeof(FatInlineAtom) == sizeof(JSFatInlineString) + sizeof(uint64_t),
+ "FatInlineAtom must have size of a fat inline string + HashNumber, "
+ "aligned to gc::CellSize");
+
+} // namespace js
+
+inline js::HashNumber
+JSAtom::hash() const
+{
+ if (isFatInline())
+ return static_cast<const js::FatInlineAtom*>(this)->hash();
+ return static_cast<const js::NormalAtom*>(this)->hash();
+}
+
+inline void
+JSAtom::initHash(js::HashNumber hash)
+{
+ if (isFatInline())
+ return static_cast<js::FatInlineAtom*>(this)->initHash(hash);
+ return static_cast<js::NormalAtom*>(this)->initHash(hash);
+}
+
+MOZ_ALWAYS_INLINE JSAtom*
+JSFlatString::morphAtomizedStringIntoAtom(js::HashNumber hash)
+{
+ d.u1.flags |= ATOM_BIT;
+ JSAtom* atom = &asAtom();
+ atom->initHash(hash);
+ return atom;
+}
+
+MOZ_ALWAYS_INLINE JSAtom*
+JSFlatString::morphAtomizedStringIntoPermanentAtom(js::HashNumber hash)
+{
+ d.u1.flags |= PERMANENT_ATOM_MASK;
+ JSAtom* atom = &asAtom();
+ atom->initHash(hash);
+ return atom;
+}
+
+namespace js {
+
+class StaticStrings
+{
+ private:
+ /* Bigger chars cannot be in a length-2 string. */
+ static const size_t SMALL_CHAR_LIMIT = 128U;
+ static const size_t NUM_SMALL_CHARS = 64U;
+
+ JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
+
+ public:
+ /* We keep these public for the JITs. */
+ static const size_t UNIT_STATIC_LIMIT = 256U;
+ JSAtom* unitStaticTable[UNIT_STATIC_LIMIT];
+
+ static const size_t INT_STATIC_LIMIT = 256U;
+ JSAtom* intStaticTable[INT_STATIC_LIMIT];
+
+ StaticStrings() {
+ mozilla::PodZero(this);
+ }
+
+ bool init(JSContext* cx);
+ void trace(JSTracer* trc);
+
+ static bool hasUint(uint32_t u) { return u < INT_STATIC_LIMIT; }
+
+ JSAtom* getUint(uint32_t u) {
+ MOZ_ASSERT(hasUint(u));
+ return intStaticTable[u];
+ }
+
+ static bool hasInt(int32_t i) {
+ return uint32_t(i) < INT_STATIC_LIMIT;
+ }
+
+ JSAtom* getInt(int32_t i) {
+ MOZ_ASSERT(hasInt(i));
+ return getUint(uint32_t(i));
+ }
+
+ static bool hasUnit(char16_t c) { return c < UNIT_STATIC_LIMIT; }
+
+ JSAtom* getUnit(char16_t c) {
+ MOZ_ASSERT(hasUnit(c));
+ return unitStaticTable[c];
+ }
+
+ /* May not return atom, returns null on (reported) failure. */
+ inline JSLinearString* getUnitStringForElement(JSContext* cx, JSString* str, size_t index);
+
+ template <typename CharT>
+ static bool isStatic(const CharT* chars, size_t len);
+ static bool isStatic(JSAtom* atom);
+
+ /* Return null if no static atom exists for the given (chars, length). */
+ template <typename CharT>
+ JSAtom* lookup(const CharT* chars, size_t length) {
+ switch (length) {
+ case 1: {
+ char16_t c = chars[0];
+ if (c < UNIT_STATIC_LIMIT)
+ return getUnit(c);
+ return nullptr;
+ }
+ case 2:
+ if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
+ return getLength2(chars[0], chars[1]);
+ return nullptr;
+ case 3:
+ /*
+ * Here we know that JSString::intStringTable covers only 256 (or at least
+ * not 1000 or more) chars. We rely on order here to resolve the unit vs.
+ * int string/length-2 string atom identity issue by giving priority to unit
+ * strings for "0" through "9" and length-2 strings for "10" through "99".
+ */
+ static_assert(INT_STATIC_LIMIT <= 999,
+ "static int strings assumed below to be at most "
+ "three digits");
+ if ('1' <= chars[0] && chars[0] <= '9' &&
+ '0' <= chars[1] && chars[1] <= '9' &&
+ '0' <= chars[2] && chars[2] <= '9') {
+ int i = (chars[0] - '0') * 100 +
+ (chars[1] - '0') * 10 +
+ (chars[2] - '0');
+
+ if (unsigned(i) < INT_STATIC_LIMIT)
+ return getInt(i);
+ }
+ return nullptr;
+ }
+
+ return nullptr;
+ }
+
+ private:
+ typedef uint8_t SmallChar;
+ static const SmallChar INVALID_SMALL_CHAR = -1;
+
+ static bool fitsInSmallChar(char16_t c) {
+ return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
+ }
+
+ static const SmallChar toSmallChar[];
+
+ JSAtom* getLength2(char16_t c1, char16_t c2);
+ JSAtom* getLength2(uint32_t u) {
+ MOZ_ASSERT(u < 100);
+ return getLength2('0' + u / 10, '0' + u % 10);
+ }
+};
+
+/*
+ * Represents an atomized string which does not contain an index (that is, an
+ * unsigned 32-bit value). Thus for any PropertyName propname,
+ * ToString(ToUint32(propname)) never equals propname.
+ *
+ * To more concretely illustrate the utility of PropertyName, consider that it
+ * is used to partition, in a type-safe manner, the ways to refer to a
+ * property, as follows:
+ *
+ * - uint32_t indexes,
+ * - PropertyName strings which don't encode uint32_t indexes, and
+ * - jsspecial special properties (non-ES5 properties like object-valued
+ * jsids, JSID_EMPTY, JSID_VOID, and maybe in the future Harmony-proposed
+ * private names).
+ */
+class PropertyName : public JSAtom
+{
+ private:
+ /* Vacuous and therefore unimplemented. */
+ PropertyName* asPropertyName() = delete;
+};
+
+static_assert(sizeof(PropertyName) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
+static MOZ_ALWAYS_INLINE jsid
+NameToId(PropertyName* name)
+{
+ return NON_INTEGER_ATOM_TO_JSID(name);
+}
+
+using PropertyNameVector = JS::GCVector<PropertyName*>;
+
+template <typename CharT>
+void
+CopyChars(CharT* dest, const JSLinearString& str);
+
+static inline UniqueChars
+StringToNewUTF8CharsZ(ExclusiveContext* maybecx, JSString& str)
+{
+ JS::AutoCheckCannotGC nogc;
+
+ JSLinearString* linear = str.ensureLinear(maybecx);
+ if (!linear)
+ return nullptr;
+
+ return UniqueChars(linear->hasLatin1Chars()
+ ? JS::CharsToNewUTF8CharsZ(maybecx, linear->latin1Range(nogc)).c_str()
+ : JS::CharsToNewUTF8CharsZ(maybecx, linear->twoByteRange(nogc)).c_str());
+}
+
+/* GC-allocate a string descriptor for the given malloc-allocated chars. */
+template <js::AllowGC allowGC, typename CharT>
+extern JSFlatString*
+NewString(js::ExclusiveContext* cx, CharT* chars, size_t length);
+
+/* Like NewString, but doesn't try to deflate to Latin1. */
+template <js::AllowGC allowGC, typename CharT>
+extern JSFlatString*
+NewStringDontDeflate(js::ExclusiveContext* cx, CharT* chars, size_t length);
+
+extern JSLinearString*
+NewDependentString(JSContext* cx, JSString* base, size_t start, size_t length);
+
+/* Take ownership of an array of Latin1Chars. */
+extern JSFlatString*
+NewLatin1StringZ(js::ExclusiveContext* cx, UniqueChars chars);
+
+/* Copy a counted string and GC-allocate a descriptor for it. */
+template <js::AllowGC allowGC, typename CharT>
+extern JSFlatString*
+NewStringCopyN(js::ExclusiveContext* cx, const CharT* s, size_t n);
+
+template <js::AllowGC allowGC>
+inline JSFlatString*
+NewStringCopyN(ExclusiveContext* cx, const char* s, size_t n)
+{
+ return NewStringCopyN<allowGC>(cx, reinterpret_cast<const Latin1Char*>(s), n);
+}
+
+/* Like NewStringCopyN, but doesn't try to deflate to Latin1. */
+template <js::AllowGC allowGC, typename CharT>
+extern JSFlatString*
+NewStringCopyNDontDeflate(js::ExclusiveContext* cx, const CharT* s, size_t n);
+
+/* Copy a C string and GC-allocate a descriptor for it. */
+template <js::AllowGC allowGC>
+inline JSFlatString*
+NewStringCopyZ(js::ExclusiveContext* cx, const char16_t* s)
+{
+ return NewStringCopyN<allowGC>(cx, s, js_strlen(s));
+}
+
+template <js::AllowGC allowGC>
+inline JSFlatString*
+NewStringCopyZ(js::ExclusiveContext* cx, const char* s)
+{
+ return NewStringCopyN<allowGC>(cx, s, strlen(s));
+}
+
+template <js::AllowGC allowGC>
+extern JSFlatString*
+NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8);
+
+template <js::AllowGC allowGC>
+inline JSFlatString*
+NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ utf8)
+{
+ return NewStringCopyUTF8N<allowGC>(cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str())));
+}
+
+JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
+
+} /* namespace js */
+
+// Addon IDs are interned atoms which are never destroyed. This detail is
+// not exposed outside the API.
+class JSAddonId : public JSAtom
+{};
+
+MOZ_ALWAYS_INLINE bool
+JSString::getChar(js::ExclusiveContext* cx, size_t index, char16_t* code)
+{
+ MOZ_ASSERT(index < length());
+
+ /*
+ * Optimization for one level deep ropes.
+ * This is common for the following pattern:
+ *
+ * while() {
+ * text = text.substr(0, x) + "bla" + text.substr(x)
+ * test.charCodeAt(x + 1)
+ * }
+ */
+ JSString* str;
+ if (isRope()) {
+ JSRope* rope = &asRope();
+ if (uint32_t(index) < rope->leftChild()->length()) {
+ str = rope->leftChild();
+ } else {
+ str = rope->rightChild();
+ index -= rope->leftChild()->length();
+ }
+ } else {
+ str = this;
+ }
+
+ if (!str->ensureLinear(cx))
+ return false;
+
+ *code = str->asLinear().latin1OrTwoByteChar(index);
+ return true;
+}
+
+MOZ_ALWAYS_INLINE JSLinearString*
+JSString::ensureLinear(js::ExclusiveContext* cx)
+{
+ return isLinear()
+ ? &asLinear()
+ : asRope().flatten(cx);
+}
+
+inline JSLinearString*
+JSString::base() const
+{
+ MOZ_ASSERT(hasBase());
+ MOZ_ASSERT(!d.s.u3.base->isInline());
+ return d.s.u3.base;
+}
+
+template<>
+MOZ_ALWAYS_INLINE const char16_t*
+JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC& nogc) const
+{
+ return nonInlineTwoByteChars(nogc);
+}
+
+template<>
+MOZ_ALWAYS_INLINE const JS::Latin1Char*
+JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC& nogc) const
+{
+ return nonInlineLatin1Chars(nogc);
+}
+
+template<>
+MOZ_ALWAYS_INLINE const char16_t*
+JSLinearString::chars(const JS::AutoCheckCannotGC& nogc) const
+{
+ return rawTwoByteChars();
+}
+
+template<>
+MOZ_ALWAYS_INLINE const JS::Latin1Char*
+JSLinearString::chars(const JS::AutoCheckCannotGC& nogc) const
+{
+ return rawLatin1Chars();
+}
+
+template <>
+MOZ_ALWAYS_INLINE bool
+JSRope::copyChars<JS::Latin1Char>(js::ExclusiveContext* cx,
+ js::ScopedJSFreePtr<JS::Latin1Char>& out) const
+{
+ return copyLatin1Chars(cx, out);
+}
+
+template <>
+MOZ_ALWAYS_INLINE bool
+JSRope::copyChars<char16_t>(js::ExclusiveContext* cx, js::ScopedJSFreePtr<char16_t>& out) const
+{
+ return copyTwoByteChars(cx, out);
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSThinInlineString::lengthFits<JS::Latin1Char>(size_t length)
+{
+ return length <= MAX_LENGTH_LATIN1;
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSThinInlineString::lengthFits<char16_t>(size_t length)
+{
+ return length <= MAX_LENGTH_TWO_BYTE;
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSFatInlineString::lengthFits<JS::Latin1Char>(size_t length)
+{
+ static_assert((INLINE_EXTENSION_CHARS_LATIN1 * sizeof(char)) % js::gc::CellSize == 0,
+ "fat inline strings' Latin1 characters don't exactly "
+ "fill subsequent cells and thus are wasteful");
+ static_assert(MAX_LENGTH_LATIN1 + 1 ==
+ (sizeof(JSFatInlineString) -
+ offsetof(JSFatInlineString, d.inlineStorageLatin1)) / sizeof(char),
+ "MAX_LENGTH_LATIN1 must be one less than inline Latin1 "
+ "storage count");
+
+ return length <= MAX_LENGTH_LATIN1;
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSFatInlineString::lengthFits<char16_t>(size_t length)
+{
+ static_assert((INLINE_EXTENSION_CHARS_TWO_BYTE * sizeof(char16_t)) % js::gc::CellSize == 0,
+ "fat inline strings' char16_t characters don't exactly "
+ "fill subsequent cells and thus are wasteful");
+ static_assert(MAX_LENGTH_TWO_BYTE + 1 ==
+ (sizeof(JSFatInlineString) -
+ offsetof(JSFatInlineString, d.inlineStorageTwoByte)) / sizeof(char16_t),
+ "MAX_LENGTH_TWO_BYTE must be one less than inline "
+ "char16_t storage count");
+
+ return length <= MAX_LENGTH_TWO_BYTE;
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSInlineString::lengthFits<JS::Latin1Char>(size_t length)
+{
+ // If it fits in a fat inline string, it fits in any inline string.
+ return JSFatInlineString::lengthFits<JS::Latin1Char>(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSInlineString::lengthFits<char16_t>(size_t length)
+{
+ // If it fits in a fat inline string, it fits in any inline string.
+ return JSFatInlineString::lengthFits<char16_t>(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE void
+JSString::setNonInlineChars(const char16_t* chars)
+{
+ d.s.u2.nonInlineCharsTwoByte = chars;
+}
+
+template<>
+MOZ_ALWAYS_INLINE void
+JSString::setNonInlineChars(const JS::Latin1Char* chars)
+{
+ d.s.u2.nonInlineCharsLatin1 = chars;
+}
+
+MOZ_ALWAYS_INLINE const JS::Latin1Char*
+JSLinearString::rawLatin1Chars() const
+{
+ MOZ_ASSERT(JSString::isLinear());
+ MOZ_ASSERT(hasLatin1Chars());
+ return isInline() ? d.inlineStorageLatin1 : d.s.u2.nonInlineCharsLatin1;
+}
+
+MOZ_ALWAYS_INLINE const char16_t*
+JSLinearString::rawTwoByteChars() const
+{
+ MOZ_ASSERT(JSString::isLinear());
+ MOZ_ASSERT(hasTwoByteChars());
+ return isInline() ? d.inlineStorageTwoByte : d.s.u2.nonInlineCharsTwoByte;
+}
+
+inline js::PropertyName*
+JSAtom::asPropertyName()
+{
+#ifdef DEBUG
+ uint32_t dummy;
+ MOZ_ASSERT(!isIndex(&dummy));
+#endif
+ return static_cast<js::PropertyName*>(this);
+}
+
+#endif /* vm_String_h */