/* -*- 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_inl_h #define vm_String_inl_h #include "vm/String.h" #include "mozilla/PodOperations.h" #include "mozilla/Range.h" #include "jscntxt.h" #include "jscompartment.h" #include "gc/Allocator.h" #include "gc/Marking.h" namespace js { // Allocate a thin inline string if possible, and a fat inline string if not. template static MOZ_ALWAYS_INLINE JSInlineString* AllocateInlineString(ExclusiveContext* cx, size_t len, CharT** chars) { MOZ_ASSERT(JSInlineString::lengthFits(len)); if (JSThinInlineString::lengthFits(len)) { JSThinInlineString* str = JSThinInlineString::new_(cx); if (!str) return nullptr; *chars = str->init(len); return str; } JSFatInlineString* str = JSFatInlineString::new_(cx); if (!str) return nullptr; *chars = str->init(len); return str; } // Create a thin inline string if possible, and a fat inline string if not. template static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(ExclusiveContext* cx, mozilla::Range chars) { /* * Don't bother trying to find a static atom; measurement shows that not * many get here (for one, Atomize is catching them). */ size_t len = chars.length(); CharT* storage; JSInlineString* str = AllocateInlineString(cx, len, &storage); if (!str) return nullptr; mozilla::PodCopy(storage, chars.begin().get(), len); storage[len] = 0; return str; } // Create a thin inline string if possible, and a fat inline string if not. template static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(ExclusiveContext* cx, HandleLinearString base, size_t start, size_t length) { MOZ_ASSERT(JSInlineString::lengthFits(length)); CharT* chars; JSInlineString* s = AllocateInlineString(cx, length, &chars); if (!s) return nullptr; JS::AutoCheckCannotGC nogc; mozilla::PodCopy(chars, base->chars(nogc) + start, length); chars[length] = 0; return s; } static inline void StringWriteBarrierPost(js::ExclusiveContext* maybecx, JSString** strp) { } static inline void StringWriteBarrierPostRemove(js::ExclusiveContext* maybecx, JSString** strp) { } } /* namespace js */ MOZ_ALWAYS_INLINE bool JSString::validateLength(js::ExclusiveContext* maybecx, size_t length) { if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) { js::ReportAllocationOverflow(maybecx); return false; } return true; } MOZ_ALWAYS_INLINE void JSRope::init(js::ExclusiveContext* cx, JSString* left, JSString* right, size_t length) { d.u1.length = length; d.u1.flags = ROPE_FLAGS; if (left->hasLatin1Chars() && right->hasLatin1Chars()) d.u1.flags |= LATIN1_CHARS_BIT; d.s.u2.left = left; d.s.u3.right = right; js::StringWriteBarrierPost(cx, &d.s.u2.left); js::StringWriteBarrierPost(cx, &d.s.u3.right); } template MOZ_ALWAYS_INLINE JSRope* JSRope::new_(js::ExclusiveContext* cx, typename js::MaybeRooted::HandleType left, typename js::MaybeRooted::HandleType right, size_t length) { if (!validateLength(cx, length)) return nullptr; JSRope* str = static_cast(js::Allocate(cx)); if (!str) return nullptr; str->init(cx, left, right, length); return str; } MOZ_ALWAYS_INLINE void JSDependentString::init(js::ExclusiveContext* cx, JSLinearString* base, size_t start, size_t length) { MOZ_ASSERT(start + length <= base->length()); d.u1.length = length; JS::AutoCheckCannotGC nogc; if (base->hasLatin1Chars()) { d.u1.flags = DEPENDENT_FLAGS | LATIN1_CHARS_BIT; d.s.u2.nonInlineCharsLatin1 = base->latin1Chars(nogc) + start; } else { d.u1.flags = DEPENDENT_FLAGS; d.s.u2.nonInlineCharsTwoByte = base->twoByteChars(nogc) + start; } d.s.u3.base = base; js::StringWriteBarrierPost(cx, reinterpret_cast(&d.s.u3.base)); } MOZ_ALWAYS_INLINE JSLinearString* JSDependentString::new_(js::ExclusiveContext* cx, JSLinearString* baseArg, size_t start, size_t length) { /* * Try to avoid long chains of dependent strings. We can't avoid these * entirely, however, due to how ropes are flattened. */ if (baseArg->isDependent()) { if (mozilla::Maybe offset = baseArg->asDependent().baseOffset()) { start += *offset; baseArg = baseArg->asDependent().base(); } } MOZ_ASSERT(start + length <= baseArg->length()); /* * Do not create a string dependent on inline chars from another string, * both to avoid the awkward moving-GC hazard this introduces and because it * is more efficient to immediately undepend here. */ bool useInline = baseArg->hasTwoByteChars() ? JSInlineString::lengthFits(length) : JSInlineString::lengthFits(length); if (useInline) { js::RootedLinearString base(cx, baseArg); return baseArg->hasLatin1Chars() ? js::NewInlineString(cx, base, start, length) : js::NewInlineString(cx, base, start, length); } if (baseArg->isExternal() && !baseArg->ensureFlat(cx->asJSContext())) return nullptr; JSDependentString* str = static_cast(js::Allocate(cx)); if (str) { str->init(cx, baseArg, start, length); return str; } js::RootedLinearString base(cx, baseArg); str = static_cast(js::Allocate(cx)); if (!str) return nullptr; str->init(cx, base, start, length); return str; } MOZ_ALWAYS_INLINE void JSFlatString::init(const char16_t* chars, size_t length) { d.u1.length = length; d.u1.flags = FLAT_BIT; d.s.u2.nonInlineCharsTwoByte = chars; } MOZ_ALWAYS_INLINE void JSFlatString::init(const JS::Latin1Char* chars, size_t length) { d.u1.length = length; d.u1.flags = FLAT_BIT | LATIN1_CHARS_BIT; d.s.u2.nonInlineCharsLatin1 = chars; } template MOZ_ALWAYS_INLINE JSFlatString* JSFlatString::new_(js::ExclusiveContext* cx, const CharT* chars, size_t length) { MOZ_ASSERT(chars[length] == CharT(0)); if (!validateLength(cx, length)) return nullptr; JSFlatString* str; if (cx->compartment()->isAtomsCompartment()) str = js::Allocate(cx); else str = static_cast(js::Allocate(cx)); if (!str) return nullptr; str->init(chars, length); return str; } inline js::PropertyName* JSFlatString::toPropertyName(JSContext* cx) { #ifdef DEBUG uint32_t dummy; MOZ_ASSERT(!isIndex(&dummy)); #endif if (isAtom()) return asAtom().asPropertyName(); JSAtom* atom = js::AtomizeString(cx, this); if (!atom) return nullptr; return atom->asPropertyName(); } template MOZ_ALWAYS_INLINE JSThinInlineString* JSThinInlineString::new_(js::ExclusiveContext* cx) { if (cx->compartment()->isAtomsCompartment()) return (JSThinInlineString*)(js::Allocate(cx)); return static_cast(js::Allocate(cx)); } template MOZ_ALWAYS_INLINE JSFatInlineString* JSFatInlineString::new_(js::ExclusiveContext* cx) { if (cx->compartment()->isAtomsCompartment()) return (JSFatInlineString*)(js::Allocate(cx)); return js::Allocate(cx); } template<> MOZ_ALWAYS_INLINE JS::Latin1Char* JSThinInlineString::init(size_t length) { MOZ_ASSERT(lengthFits(length)); d.u1.length = length; d.u1.flags = INIT_THIN_INLINE_FLAGS | LATIN1_CHARS_BIT; return d.inlineStorageLatin1; } template<> MOZ_ALWAYS_INLINE char16_t* JSThinInlineString::init(size_t length) { MOZ_ASSERT(lengthFits(length)); d.u1.length = length; d.u1.flags = INIT_THIN_INLINE_FLAGS; return d.inlineStorageTwoByte; } template<> MOZ_ALWAYS_INLINE JS::Latin1Char* JSFatInlineString::init(size_t length) { MOZ_ASSERT(lengthFits(length)); d.u1.length = length; d.u1.flags = INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT; return d.inlineStorageLatin1; } template<> MOZ_ALWAYS_INLINE char16_t* JSFatInlineString::init(size_t length) { MOZ_ASSERT(lengthFits(length)); d.u1.length = length; d.u1.flags = INIT_FAT_INLINE_FLAGS; return d.inlineStorageTwoByte; } MOZ_ALWAYS_INLINE void JSExternalString::init(const char16_t* chars, size_t length, const JSStringFinalizer* fin) { MOZ_ASSERT(fin); MOZ_ASSERT(fin->finalize); d.u1.length = length; d.u1.flags = EXTERNAL_FLAGS; d.s.u2.nonInlineCharsTwoByte = chars; d.s.u3.externalFinalizer = fin; } MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(JSContext* cx, const char16_t* chars, size_t length, const JSStringFinalizer* fin) { if (!validateLength(cx, length)) return nullptr; JSExternalString* str = js::Allocate(cx); if (!str) return nullptr; str->init(chars, length, fin); cx->updateMallocCounter((length + 1) * sizeof(char16_t)); return str; } inline JSLinearString* js::StaticStrings::getUnitStringForElement(JSContext* cx, JSString* str, size_t index) { MOZ_ASSERT(index < str->length()); char16_t c; if (!str->getChar(cx, index, &c)) return nullptr; if (c < UNIT_STATIC_LIMIT) return getUnit(c); return NewDependentString(cx, str, index, 1); } inline JSAtom* js::StaticStrings::getLength2(char16_t c1, char16_t c2) { MOZ_ASSERT(fitsInSmallChar(c1)); MOZ_ASSERT(fitsInSmallChar(c2)); size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]; return length2StaticTable[index]; } MOZ_ALWAYS_INLINE void JSString::finalize(js::FreeOp* fop) { /* FatInline strings are in a different arena. */ MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING); MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM); if (isFlat()) asFlat().finalize(fop); else MOZ_ASSERT(isDependent() || isRope()); } inline void JSFlatString::finalize(js::FreeOp* fop) { MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING); MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM); if (!isInline()) fop->free_(nonInlineCharsRaw()); } inline void JSFatInlineString::finalize(js::FreeOp* fop) { MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING); if (!isInline()) fop->free_(nonInlineCharsRaw()); } inline void JSAtom::finalize(js::FreeOp* fop) { MOZ_ASSERT(JSString::isAtom()); MOZ_ASSERT(JSString::isFlat()); MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::ATOM || getAllocKind() == js::gc::AllocKind::FAT_INLINE_ATOM); if (!isInline()) fop->free_(nonInlineCharsRaw()); } inline void JSExternalString::finalize(js::FreeOp* fop) { if (!JSString::isExternal()) { // This started out as an external string, but was turned into a // non-external string by JSExternalString::ensureFlat. MOZ_ASSERT(isFlat()); fop->free_(nonInlineCharsRaw()); return; } const JSStringFinalizer* fin = externalFinalizer(); fin->finalize(zone(), fin, const_cast(rawTwoByteChars())); } #endif /* vm_String_inl_h */