1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "util/StringBuffer.h"
9 #include "mozilla/Latin1.h"
10 #include "mozilla/Range.h"
14 #include "frontend/ParserAtom.h" // frontend::{ParserAtomsTable, TaggedParserAtomIndex
15 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
16 #include "vm/BigIntType.h"
17 #include "vm/StaticStrings.h"
19 #include "vm/JSObject-inl.h"
20 #include "vm/StringType-inl.h"
24 template <typename CharT
, class Buffer
>
25 static CharT
* ExtractWellSized(Buffer
& cb
) {
26 size_t capacity
= cb
.capacity();
27 size_t length
= cb
.length();
28 StringBufferAllocPolicy allocPolicy
= cb
.allocPolicy();
30 CharT
* buf
= cb
.extractOrCopyRawBuffer();
35 // For medium/big buffers, avoid wasting more than 1/4 of the memory. Very
36 // small strings will not reach here because they will have been stored in a
37 // JSInlineString. Don't bother shrinking the allocation unless at least 80
38 // bytes will be saved, which is a somewhat arbitrary number (though it does
39 // correspond to a mozjemalloc size class.)
40 MOZ_ASSERT(capacity
>= length
);
41 constexpr size_t minCharsToReclaim
= 80 / sizeof(CharT
);
42 if (capacity
- length
>= minCharsToReclaim
&&
43 capacity
- length
> capacity
/ 4) {
44 CharT
* tmp
= allocPolicy
.pod_realloc
<CharT
>(buf
, capacity
, length
);
46 allocPolicy
.free_(buf
);
55 char16_t
* StringBuffer::stealChars() {
56 if (isLatin1() && !inflateChars()) {
60 return ExtractWellSized
<char16_t
>(twoByteChars());
63 bool StringBuffer::inflateChars() {
64 MOZ_ASSERT(isLatin1());
66 TwoByteCharBuffer
twoByte(latin1Chars().allocPolicy());
69 * Note: we don't use Vector::capacity() because it always returns a
70 * value >= sInlineCapacity. Since Latin1CharBuffer::sInlineCapacity >
71 * TwoByteCharBuffer::sInlineCapacitychars, we'd always malloc here.
73 size_t capacity
= std::max(reserved_
, latin1Chars().length());
74 if (!twoByte
.reserve(capacity
)) {
78 twoByte
.infallibleGrowByUninitialized(latin1Chars().length());
80 mozilla::ConvertLatin1toUtf16(mozilla::AsChars(latin1Chars()), twoByte
);
83 cb
.construct
<TwoByteCharBuffer
>(std::move(twoByte
));
87 bool StringBuffer::append(const frontend::ParserAtomsTable
& parserAtoms
,
88 frontend::TaggedParserAtomIndex atom
) {
89 return parserAtoms
.appendTo(*this, atom
);
92 template <typename CharT
>
93 JSLinearString
* StringBuffer::finishStringInternal(JSContext
* cx
,
95 size_t len
= length();
97 if (JSAtom
* staticStr
= cx
->staticStrings().lookup(begin
<CharT
>(), len
)) {
101 if (JSInlineString::lengthFits
<CharT
>(len
)) {
102 mozilla::Range
<const CharT
> range(begin
<CharT
>(), len
);
103 return NewInlineString
<CanGC
>(cx
, range
);
106 UniquePtr
<CharT
[], JS::FreePolicy
> buf(
107 ExtractWellSized
<CharT
>(chars
<CharT
>()));
113 JSLinearString
* str
=
114 NewStringDontDeflate
<CanGC
>(cx
, std::move(buf
), len
, heap
);
122 JSLinearString
* JSStringBuilder::finishString(gc::Heap heap
) {
123 MOZ_ASSERT(maybeCx_
);
125 size_t len
= length();
127 return maybeCx_
->names().empty_
;
130 if (MOZ_UNLIKELY(!JSString::validateLength(maybeCx_
, len
))) {
134 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE
<
135 TwoByteCharBuffer::InlineLength
);
136 static_assert(JSFatInlineString::MAX_LENGTH_LATIN1
<
137 Latin1CharBuffer::InlineLength
);
139 return isLatin1() ? finishStringInternal
<Latin1Char
>(maybeCx_
, heap
)
140 : finishStringInternal
<char16_t
>(maybeCx_
, heap
);
143 JSAtom
* StringBuffer::finishAtom() {
144 MOZ_ASSERT(maybeCx_
);
146 size_t len
= length();
148 return maybeCx_
->names().empty_
;
152 JSAtom
* atom
= AtomizeChars(maybeCx_
, latin1Chars().begin(), len
);
153 latin1Chars().clear();
157 JSAtom
* atom
= AtomizeChars(maybeCx_
, twoByteChars().begin(), len
);
158 twoByteChars().clear();
162 frontend::TaggedParserAtomIndex
StringBuffer::finishParserAtom(
163 frontend::ParserAtomsTable
& parserAtoms
, FrontendContext
* fc
) {
164 size_t len
= length();
166 return frontend::TaggedParserAtomIndex::WellKnown::empty();
170 auto result
= parserAtoms
.internLatin1(fc
, latin1Chars().begin(), len
);
171 latin1Chars().clear();
175 auto result
= parserAtoms
.internChar16(fc
, twoByteChars().begin(), len
);
176 twoByteChars().clear();
180 bool js::ValueToStringBufferSlow(JSContext
* cx
, const Value
& arg
,
182 RootedValue
v(cx
, arg
);
183 if (!ToPrimitive(cx
, JSTYPE_STRING
, &v
)) {
188 return sb
.append(v
.toString());
191 return NumberValueToStringBuffer(v
, sb
);
194 return BooleanToStringBuffer(v
.toBoolean(), sb
);
197 return sb
.append(cx
->names().null
);
200 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
201 JSMSG_SYMBOL_TO_STRING
);
205 RootedBigInt
i(cx
, v
.toBigInt());
206 JSLinearString
* str
= BigInt::toString
<CanGC
>(cx
, i
, 10);
210 return sb
.append(str
);
212 MOZ_ASSERT(v
.isUndefined());
213 return sb
.append(cx
->names().undefined
);