michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef vm_String_inl_h michael@0: #define vm_String_inl_h michael@0: michael@0: #include "vm/String.h" michael@0: michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "jscntxt.h" michael@0: michael@0: #include "gc/Marking.h" michael@0: michael@0: #include "jsgcinlines.h" michael@0: michael@0: namespace js { michael@0: michael@0: template michael@0: static MOZ_ALWAYS_INLINE JSInlineString * michael@0: NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars) michael@0: { michael@0: size_t len = chars.length(); michael@0: JS_ASSERT(JSFatInlineString::lengthFits(len)); michael@0: JSInlineString *str = JSInlineString::lengthFits(len) michael@0: ? JSInlineString::new_(cx) michael@0: : JSFatInlineString::new_(cx); michael@0: if (!str) michael@0: return nullptr; michael@0: michael@0: jschar *p = str->init(len); michael@0: for (size_t i = 0; i < len; ++i) michael@0: p[i] = static_cast(chars[i]); michael@0: p[len] = '\0'; michael@0: return str; michael@0: } michael@0: michael@0: template michael@0: static MOZ_ALWAYS_INLINE JSInlineString * michael@0: NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars) michael@0: { michael@0: size_t len = chars.length(); michael@0: michael@0: /* michael@0: * Don't bother trying to find a static atom; measurement shows that not michael@0: * many get here (for one, Atomize is catching them). michael@0: */ michael@0: JS_ASSERT(JSFatInlineString::lengthFits(len)); michael@0: JSInlineString *str = JSInlineString::lengthFits(len) michael@0: ? JSInlineString::new_(cx) michael@0: : JSFatInlineString::new_(cx); michael@0: if (!str) michael@0: return nullptr; michael@0: michael@0: jschar *storage = str->init(len); michael@0: mozilla::PodCopy(storage, chars.start().get(), len); michael@0: storage[len] = 0; michael@0: return str; michael@0: } michael@0: michael@0: static inline void michael@0: StringWriteBarrierPost(js::ThreadSafeContext *maybecx, JSString **strp) michael@0: { michael@0: } michael@0: michael@0: static inline void michael@0: StringWriteBarrierPostRemove(js::ThreadSafeContext *maybecx, JSString **strp) michael@0: { michael@0: } michael@0: michael@0: } /* namespace js */ michael@0: michael@0: MOZ_ALWAYS_INLINE bool michael@0: JSString::validateLength(js::ThreadSafeContext *maybecx, size_t length) michael@0: { michael@0: if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) { michael@0: js_ReportAllocationOverflow(maybecx); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: JSRope::init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t length) michael@0: { michael@0: d.lengthAndFlags = buildLengthAndFlags(length, ROPE_FLAGS); michael@0: d.u1.left = left; michael@0: d.s.u2.right = right; michael@0: js::StringWriteBarrierPost(cx, &d.u1.left); michael@0: js::StringWriteBarrierPost(cx, &d.s.u2.right); michael@0: } michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE JSRope * michael@0: JSRope::new_(js::ThreadSafeContext *cx, michael@0: typename js::MaybeRooted::HandleType left, michael@0: typename js::MaybeRooted::HandleType right, michael@0: size_t length) michael@0: { michael@0: if (!validateLength(cx, length)) michael@0: return nullptr; michael@0: JSRope *str = (JSRope *) js_NewGCString(cx); michael@0: if (!str) michael@0: return nullptr; michael@0: str->init(cx, left, right, length); michael@0: return str; michael@0: } michael@0: michael@0: inline void michael@0: JSRope::markChildren(JSTracer *trc) michael@0: { michael@0: js::gc::MarkStringUnbarriered(trc, &d.u1.left, "left child"); michael@0: js::gc::MarkStringUnbarriered(trc, &d.s.u2.right, "right child"); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: JSDependentString::init(js::ThreadSafeContext *cx, JSLinearString *base, const jschar *chars, michael@0: size_t length) michael@0: { michael@0: JS_ASSERT(!js::IsPoisonedPtr(base)); michael@0: d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_FLAGS); michael@0: d.u1.chars = chars; michael@0: d.s.u2.base = base; michael@0: js::StringWriteBarrierPost(cx, reinterpret_cast(&d.s.u2.base)); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE JSLinearString * michael@0: JSDependentString::new_(js::ExclusiveContext *cx, michael@0: JSLinearString *baseArg, const jschar *chars, size_t length) michael@0: { michael@0: /* Try to avoid long chains of dependent strings. */ michael@0: while (baseArg->isDependent()) michael@0: baseArg = baseArg->asDependent().base(); michael@0: michael@0: JS_ASSERT(baseArg->isFlat()); michael@0: michael@0: /* michael@0: * The chars we are pointing into must be owned by something in the chain michael@0: * of dependent or undepended strings kept alive by our base pointer. michael@0: */ michael@0: #ifdef DEBUG michael@0: for (JSLinearString *b = baseArg; ; b = b->base()) { michael@0: if (chars >= b->chars() && chars < b->chars() + b->length() && michael@0: length <= b->length() - (chars - b->chars())) michael@0: { michael@0: break; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * Do not create a string dependent on inline chars from another string, michael@0: * both to avoid the awkward moving-GC hazard this introduces and because it michael@0: * is more efficient to immediately undepend here. michael@0: */ michael@0: if (JSFatInlineString::lengthFits(length)) michael@0: return js::NewFatInlineString(cx, JS::TwoByteChars(chars, length)); michael@0: michael@0: JSDependentString *str = (JSDependentString *)js_NewGCString(cx); michael@0: if (str) { michael@0: str->init(cx, baseArg, chars, length); michael@0: return str; michael@0: } michael@0: michael@0: JS::Rooted base(cx, baseArg); michael@0: michael@0: str = (JSDependentString *)js_NewGCString(cx); michael@0: if (!str) michael@0: return nullptr; michael@0: str->init(cx, base, chars, length); michael@0: return str; michael@0: } michael@0: michael@0: inline void michael@0: JSString::markBase(JSTracer *trc) michael@0: { michael@0: JS_ASSERT(hasBase()); michael@0: js::gc::MarkStringUnbarriered(trc, &d.s.u2.base, "base"); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: JSFlatString::init(const jschar *chars, size_t length) michael@0: { michael@0: d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); michael@0: d.u1.chars = chars; michael@0: } michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE JSFlatString * michael@0: JSFlatString::new_(js::ThreadSafeContext *cx, const jschar *chars, size_t length) michael@0: { michael@0: JS_ASSERT(chars[length] == jschar(0)); michael@0: michael@0: if (!validateLength(cx, length)) michael@0: return nullptr; michael@0: JSFlatString *str = (JSFlatString *)js_NewGCString(cx); michael@0: if (!str) michael@0: return nullptr; michael@0: str->init(chars, length); michael@0: return str; michael@0: } michael@0: michael@0: inline js::PropertyName * michael@0: JSFlatString::toPropertyName(JSContext *cx) michael@0: { michael@0: #ifdef DEBUG michael@0: uint32_t dummy; michael@0: JS_ASSERT(!isIndex(&dummy)); michael@0: #endif michael@0: if (isAtom()) michael@0: return asAtom().asPropertyName(); michael@0: JSAtom *atom = js::AtomizeString(cx, this); michael@0: if (!atom) michael@0: return nullptr; michael@0: return atom->asPropertyName(); michael@0: } michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE JSInlineString * michael@0: JSInlineString::new_(js::ThreadSafeContext *cx) michael@0: { michael@0: return (JSInlineString *)js_NewGCString(cx); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE jschar * michael@0: JSInlineString::init(size_t length) michael@0: { michael@0: d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); michael@0: d.u1.chars = d.inlineStorage; michael@0: JS_ASSERT(lengthFits(length) || (isFatInline() && JSFatInlineString::lengthFits(length))); michael@0: return d.inlineStorage; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: JSInlineString::resetLength(size_t length) michael@0: { michael@0: d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); michael@0: JS_ASSERT(lengthFits(length) || (isFatInline() && JSFatInlineString::lengthFits(length))); michael@0: } michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE JSFatInlineString * michael@0: JSFatInlineString::new_(js::ThreadSafeContext *cx) michael@0: { michael@0: return js_NewGCFatInlineString(cx); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: JSExternalString::init(const jschar *chars, size_t length, const JSStringFinalizer *fin) michael@0: { michael@0: JS_ASSERT(fin); michael@0: JS_ASSERT(fin->finalize); michael@0: d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); michael@0: d.u1.chars = chars; michael@0: d.s.u2.externalFinalizer = fin; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE JSExternalString * michael@0: JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, michael@0: const JSStringFinalizer *fin) michael@0: { michael@0: JS_ASSERT(chars[length] == 0); michael@0: michael@0: if (!validateLength(cx, length)) michael@0: return nullptr; michael@0: JSExternalString *str = js_NewGCExternalString(cx); michael@0: if (!str) michael@0: return nullptr; michael@0: str->init(chars, length, fin); michael@0: cx->runtime()->updateMallocCounter(cx->zone(), (length + 1) * sizeof(jschar)); michael@0: return str; michael@0: } michael@0: michael@0: inline JSLinearString * michael@0: js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index) michael@0: { michael@0: JS_ASSERT(index < str->length()); michael@0: michael@0: jschar c; michael@0: if (!str->getChar(cx, index, &c)) michael@0: return nullptr; michael@0: if (c < UNIT_STATIC_LIMIT) michael@0: return getUnit(c); michael@0: return js_NewDependentString(cx, str, index, 1); michael@0: } michael@0: michael@0: inline JSAtom * michael@0: js::StaticStrings::getLength2(jschar c1, jschar c2) michael@0: { michael@0: JS_ASSERT(fitsInSmallChar(c1)); michael@0: JS_ASSERT(fitsInSmallChar(c2)); michael@0: size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]; michael@0: return length2StaticTable[index]; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: JSString::finalize(js::FreeOp *fop) michael@0: { michael@0: /* FatInline strings are in a different arena. */ michael@0: JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING); michael@0: michael@0: if (isFlat()) michael@0: asFlat().finalize(fop); michael@0: else michael@0: JS_ASSERT(isDependent() || isRope()); michael@0: } michael@0: michael@0: inline void michael@0: JSFlatString::finalize(js::FreeOp *fop) michael@0: { michael@0: JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING); michael@0: michael@0: if (chars() != d.inlineStorage) michael@0: fop->free_(const_cast(chars())); michael@0: } michael@0: michael@0: inline void michael@0: JSFatInlineString::finalize(js::FreeOp *fop) michael@0: { michael@0: JS_ASSERT(getAllocKind() == js::gc::FINALIZE_FAT_INLINE_STRING); michael@0: michael@0: if (chars() != d.inlineStorage) michael@0: fop->free_(const_cast(chars())); michael@0: } michael@0: michael@0: inline void michael@0: JSAtom::finalize(js::FreeOp *fop) michael@0: { michael@0: JS_ASSERT(JSString::isAtom()); michael@0: JS_ASSERT(JSString::isFlat()); michael@0: michael@0: if (chars() != d.inlineStorage) michael@0: fop->free_(const_cast(chars())); michael@0: } michael@0: michael@0: inline void michael@0: JSExternalString::finalize(js::FreeOp *fop) michael@0: { michael@0: const JSStringFinalizer *fin = externalFinalizer(); michael@0: fin->finalize(fin, const_cast(chars())); michael@0: } michael@0: michael@0: #endif /* vm_String_inl_h */