Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 :
7 : #ifndef vm_String_inl_h
8 : #define vm_String_inl_h
9 :
10 : #include "vm/String.h"
11 :
12 : #include "mozilla/PodOperations.h"
13 : #include "mozilla/Range.h"
14 :
15 : #include "jscntxt.h"
16 : #include "jscompartment.h"
17 :
18 : #include "gc/Allocator.h"
19 : #include "gc/Marking.h"
20 :
21 : namespace js {
22 :
23 : // Allocate a thin inline string if possible, and a fat inline string if not.
24 : template <AllowGC allowGC, typename CharT>
25 : static MOZ_ALWAYS_INLINE JSInlineString*
26 69287 : AllocateInlineString(JSContext* cx, size_t len, CharT** chars)
27 : {
28 69287 : MOZ_ASSERT(JSInlineString::lengthFits<CharT>(len));
29 :
30 69287 : if (JSThinInlineString::lengthFits<CharT>(len)) {
31 51392 : JSThinInlineString* str = JSThinInlineString::new_<allowGC>(cx);
32 51392 : if (!str)
33 0 : return nullptr;
34 51392 : *chars = str->init<CharT>(len);
35 51392 : return str;
36 : }
37 :
38 17895 : JSFatInlineString* str = JSFatInlineString::new_<allowGC>(cx);
39 17895 : if (!str)
40 0 : return nullptr;
41 17895 : *chars = str->init<CharT>(len);
42 17895 : return str;
43 : }
44 :
45 : // Create a thin inline string if possible, and a fat inline string if not.
46 : template <AllowGC allowGC, typename CharT>
47 : static MOZ_ALWAYS_INLINE JSInlineString*
48 48432 : NewInlineString(JSContext* cx, mozilla::Range<const CharT> chars)
49 : {
50 : /*
51 : * Don't bother trying to find a static atom; measurement shows that not
52 : * many get here (for one, Atomize is catching them).
53 : */
54 :
55 48432 : size_t len = chars.length();
56 : CharT* storage;
57 48432 : JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage);
58 48432 : if (!str)
59 0 : return nullptr;
60 :
61 48432 : mozilla::PodCopy(storage, chars.begin().get(), len);
62 48432 : storage[len] = 0;
63 48432 : return str;
64 : }
65 :
66 : // Create a thin inline string if possible, and a fat inline string if not.
67 : template <typename CharT>
68 : static MOZ_ALWAYS_INLINE JSInlineString*
69 284 : NewInlineString(JSContext* cx, HandleLinearString base, size_t start, size_t length)
70 : {
71 284 : MOZ_ASSERT(JSInlineString::lengthFits<CharT>(length));
72 :
73 : CharT* chars;
74 284 : JSInlineString* s = AllocateInlineString<CanGC>(cx, length, &chars);
75 284 : if (!s)
76 0 : return nullptr;
77 :
78 568 : JS::AutoCheckCannotGC nogc;
79 284 : mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
80 284 : chars[length] = 0;
81 284 : return s;
82 : }
83 :
84 : static inline void
85 9614 : StringWriteBarrierPost(JSContext* maybecx, JSString** strp)
86 : {
87 9614 : }
88 :
89 : static inline void
90 4842 : StringWriteBarrierPostRemove(JSContext* maybecx, JSString** strp)
91 : {
92 4842 : }
93 :
94 : } /* namespace js */
95 :
96 : MOZ_ALWAYS_INLINE bool
97 117789 : JSString::validateLength(JSContext* maybecx, size_t length)
98 : {
99 117789 : if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) {
100 0 : js::ReportAllocationOverflow(maybecx);
101 0 : return false;
102 : }
103 :
104 117789 : return true;
105 : }
106 :
107 : MOZ_ALWAYS_INLINE void
108 3719 : JSRope::init(JSContext* cx, JSString* left, JSString* right, size_t length)
109 : {
110 3719 : d.u1.length = length;
111 3719 : d.u1.flags = ROPE_FLAGS;
112 3719 : if (left->hasLatin1Chars() && right->hasLatin1Chars())
113 3541 : d.u1.flags |= LATIN1_CHARS_BIT;
114 3719 : d.s.u2.left = left;
115 3719 : d.s.u3.right = right;
116 3719 : js::StringWriteBarrierPost(cx, &d.s.u2.left);
117 3719 : js::StringWriteBarrierPost(cx, &d.s.u3.right);
118 3719 : }
119 :
120 : template <js::AllowGC allowGC>
121 : MOZ_ALWAYS_INLINE JSRope*
122 3719 : JSRope::new_(JSContext* cx,
123 : typename js::MaybeRooted<JSString*, allowGC>::HandleType left,
124 : typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
125 : size_t length)
126 : {
127 3719 : if (!validateLength(cx, length))
128 0 : return nullptr;
129 3719 : JSRope* str = static_cast<JSRope*>(js::Allocate<JSString, allowGC>(cx));
130 3719 : if (!str)
131 0 : return nullptr;
132 3719 : str->init(cx, left, right, length);
133 3719 : return str;
134 : }
135 :
136 : MOZ_ALWAYS_INLINE void
137 50 : JSDependentString::init(JSContext* cx, JSLinearString* base, size_t start,
138 : size_t length)
139 : {
140 50 : MOZ_ASSERT(start + length <= base->length());
141 50 : d.u1.length = length;
142 100 : JS::AutoCheckCannotGC nogc;
143 50 : if (base->hasLatin1Chars()) {
144 43 : d.u1.flags = DEPENDENT_FLAGS | LATIN1_CHARS_BIT;
145 43 : d.s.u2.nonInlineCharsLatin1 = base->latin1Chars(nogc) + start;
146 : } else {
147 7 : d.u1.flags = DEPENDENT_FLAGS;
148 7 : d.s.u2.nonInlineCharsTwoByte = base->twoByteChars(nogc) + start;
149 : }
150 50 : d.s.u3.base = base;
151 50 : js::StringWriteBarrierPost(cx, reinterpret_cast<JSString**>(&d.s.u3.base));
152 50 : }
153 :
154 : MOZ_ALWAYS_INLINE JSLinearString*
155 334 : JSDependentString::new_(JSContext* cx, JSLinearString* baseArg, size_t start,
156 : size_t length)
157 : {
158 : /*
159 : * Try to avoid long chains of dependent strings. We can't avoid these
160 : * entirely, however, due to how ropes are flattened.
161 : */
162 334 : if (baseArg->isDependent()) {
163 26 : if (mozilla::Maybe<size_t> offset = baseArg->asDependent().baseOffset()) {
164 13 : start += *offset;
165 13 : baseArg = baseArg->asDependent().base();
166 : }
167 : }
168 :
169 334 : MOZ_ASSERT(start + length <= baseArg->length());
170 :
171 : /*
172 : * Do not create a string dependent on inline chars from another string,
173 : * both to avoid the awkward moving-GC hazard this introduces and because it
174 : * is more efficient to immediately undepend here.
175 : */
176 334 : bool useInline = baseArg->hasTwoByteChars()
177 334 : ? JSInlineString::lengthFits<char16_t>(length)
178 334 : : JSInlineString::lengthFits<JS::Latin1Char>(length);
179 334 : if (useInline) {
180 568 : js::RootedLinearString base(cx, baseArg);
181 284 : return baseArg->hasLatin1Chars()
182 1130 : ? js::NewInlineString<JS::Latin1Char>(cx, base, start, length)
183 850 : : js::NewInlineString<char16_t>(cx, base, start, length);
184 : }
185 :
186 50 : if (baseArg->isExternal() && !baseArg->ensureFlat(cx))
187 0 : return nullptr;
188 :
189 50 : JSDependentString* str = static_cast<JSDependentString*>(js::Allocate<JSString, js::NoGC>(cx));
190 50 : if (str) {
191 50 : str->init(cx, baseArg, start, length);
192 50 : return str;
193 : }
194 :
195 0 : js::RootedLinearString base(cx, baseArg);
196 :
197 0 : str = static_cast<JSDependentString*>(js::Allocate<JSString>(cx));
198 0 : if (!str)
199 0 : return nullptr;
200 0 : str->init(cx, base, start, length);
201 0 : return str;
202 : }
203 :
204 : MOZ_ALWAYS_INLINE void
205 68 : JSFlatString::init(const char16_t* chars, size_t length)
206 : {
207 68 : d.u1.length = length;
208 68 : d.u1.flags = FLAT_BIT;
209 68 : d.s.u2.nonInlineCharsTwoByte = chars;
210 68 : }
211 :
212 : MOZ_ALWAYS_INLINE void
213 17503 : JSFlatString::init(const JS::Latin1Char* chars, size_t length)
214 : {
215 17503 : d.u1.length = length;
216 17503 : d.u1.flags = FLAT_BIT | LATIN1_CHARS_BIT;
217 17503 : d.s.u2.nonInlineCharsLatin1 = chars;
218 17503 : }
219 :
220 : template <js::AllowGC allowGC, typename CharT>
221 : MOZ_ALWAYS_INLINE JSFlatString*
222 17569 : JSFlatString::new_(JSContext* cx, const CharT* chars, size_t length)
223 : {
224 17569 : MOZ_ASSERT(chars[length] == CharT(0));
225 :
226 17569 : if (!validateLength(cx, length))
227 0 : return nullptr;
228 :
229 : JSFlatString* str;
230 17569 : if (cx->compartment()->isAtomsCompartment())
231 13247 : str = js::Allocate<js::NormalAtom, allowGC>(cx);
232 : else
233 4322 : str = static_cast<JSFlatString*>(js::Allocate<JSString, allowGC>(cx));
234 17569 : if (!str)
235 0 : return nullptr;
236 :
237 17569 : str->init(chars, length);
238 17569 : return str;
239 : }
240 :
241 : inline js::PropertyName*
242 : JSFlatString::toPropertyName(JSContext* cx)
243 : {
244 : #ifdef DEBUG
245 : uint32_t dummy;
246 : MOZ_ASSERT(!isIndex(&dummy));
247 : #endif
248 : if (isAtom())
249 : return asAtom().asPropertyName();
250 : JSAtom* atom = js::AtomizeString(cx, this);
251 : if (!atom)
252 : return nullptr;
253 : return atom->asPropertyName();
254 : }
255 :
256 : template <js::AllowGC allowGC>
257 : MOZ_ALWAYS_INLINE JSThinInlineString*
258 51442 : JSThinInlineString::new_(JSContext* cx)
259 : {
260 51442 : if (cx->compartment()->isAtomsCompartment())
261 46510 : return (JSThinInlineString*)(js::Allocate<js::NormalAtom, allowGC>(cx));
262 :
263 4932 : return static_cast<JSThinInlineString*>(js::Allocate<JSString, allowGC>(cx));
264 : }
265 :
266 : template <js::AllowGC allowGC>
267 : MOZ_ALWAYS_INLINE JSFatInlineString*
268 17919 : JSFatInlineString::new_(JSContext* cx)
269 : {
270 17919 : if (cx->compartment()->isAtomsCompartment())
271 16094 : return (JSFatInlineString*)(js::Allocate<js::FatInlineAtom, allowGC>(cx));
272 :
273 1825 : return js::Allocate<JSFatInlineString, allowGC>(cx);
274 : }
275 :
276 : template<>
277 : MOZ_ALWAYS_INLINE JS::Latin1Char*
278 51411 : JSThinInlineString::init<JS::Latin1Char>(size_t length)
279 : {
280 51411 : MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
281 51411 : d.u1.length = length;
282 51411 : d.u1.flags = INIT_THIN_INLINE_FLAGS | LATIN1_CHARS_BIT;
283 51411 : return d.inlineStorageLatin1;
284 : }
285 :
286 : template<>
287 : MOZ_ALWAYS_INLINE char16_t*
288 31 : JSThinInlineString::init<char16_t>(size_t length)
289 : {
290 31 : MOZ_ASSERT(lengthFits<char16_t>(length));
291 31 : d.u1.length = length;
292 31 : d.u1.flags = INIT_THIN_INLINE_FLAGS;
293 31 : return d.inlineStorageTwoByte;
294 : }
295 :
296 : template<>
297 : MOZ_ALWAYS_INLINE JS::Latin1Char*
298 17902 : JSFatInlineString::init<JS::Latin1Char>(size_t length)
299 : {
300 17902 : MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
301 17902 : d.u1.length = length;
302 17902 : d.u1.flags = INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT;
303 17902 : return d.inlineStorageLatin1;
304 : }
305 :
306 : template<>
307 : MOZ_ALWAYS_INLINE char16_t*
308 17 : JSFatInlineString::init<char16_t>(size_t length)
309 : {
310 17 : MOZ_ASSERT(lengthFits<char16_t>(length));
311 17 : d.u1.length = length;
312 17 : d.u1.flags = INIT_FAT_INLINE_FLAGS;
313 17 : return d.inlineStorageTwoByte;
314 : }
315 :
316 : MOZ_ALWAYS_INLINE void
317 355 : JSExternalString::init(const char16_t* chars, size_t length, const JSStringFinalizer* fin)
318 : {
319 355 : MOZ_ASSERT(fin);
320 355 : MOZ_ASSERT(fin->finalize);
321 355 : d.u1.length = length;
322 355 : d.u1.flags = EXTERNAL_FLAGS;
323 355 : d.s.u2.nonInlineCharsTwoByte = chars;
324 355 : d.s.u3.externalFinalizer = fin;
325 355 : }
326 :
327 : MOZ_ALWAYS_INLINE JSExternalString*
328 355 : JSExternalString::new_(JSContext* cx, const char16_t* chars, size_t length,
329 : const JSStringFinalizer* fin)
330 : {
331 355 : if (!validateLength(cx, length))
332 0 : return nullptr;
333 355 : JSExternalString* str = js::Allocate<JSExternalString>(cx);
334 355 : if (!str)
335 0 : return nullptr;
336 355 : str->init(chars, length, fin);
337 355 : cx->updateMallocCounter((length + 1) * sizeof(char16_t));
338 355 : return str;
339 : }
340 :
341 : inline JSLinearString*
342 36 : js::StaticStrings::getUnitStringForElement(JSContext* cx, JSString* str, size_t index)
343 : {
344 36 : MOZ_ASSERT(index < str->length());
345 :
346 : char16_t c;
347 36 : if (!str->getChar(cx, index, &c))
348 0 : return nullptr;
349 36 : if (c < UNIT_STATIC_LIMIT)
350 34 : return getUnit(c);
351 2 : return NewDependentString(cx, str, index, 1);
352 : }
353 :
354 : MOZ_ALWAYS_INLINE void
355 0 : JSString::finalize(js::FreeOp* fop)
356 : {
357 : /* FatInline strings are in a different arena. */
358 0 : MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
359 0 : MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
360 :
361 0 : if (isFlat())
362 0 : asFlat().finalize(fop);
363 : else
364 0 : MOZ_ASSERT(isDependent() || isRope());
365 0 : }
366 :
367 : inline void
368 0 : JSFlatString::finalize(js::FreeOp* fop)
369 : {
370 0 : MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
371 0 : MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
372 :
373 0 : if (!isInline())
374 0 : fop->free_(nonInlineCharsRaw());
375 0 : }
376 :
377 : inline void
378 0 : JSFatInlineString::finalize(js::FreeOp* fop)
379 : {
380 0 : MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING);
381 :
382 0 : if (!isInline())
383 0 : fop->free_(nonInlineCharsRaw());
384 0 : }
385 :
386 : inline void
387 0 : JSAtom::finalize(js::FreeOp* fop)
388 : {
389 0 : MOZ_ASSERT(JSString::isAtom());
390 0 : MOZ_ASSERT(JSString::isFlat());
391 0 : MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::ATOM ||
392 : getAllocKind() == js::gc::AllocKind::FAT_INLINE_ATOM);
393 :
394 0 : if (!isInline())
395 0 : fop->free_(nonInlineCharsRaw());
396 0 : }
397 :
398 : inline void
399 15 : JSExternalString::finalize(js::FreeOp* fop)
400 : {
401 15 : if (!JSString::isExternal()) {
402 : // This started out as an external string, but was turned into a
403 : // non-external string by JSExternalString::ensureFlat.
404 0 : MOZ_ASSERT(isFlat());
405 0 : fop->free_(nonInlineCharsRaw());
406 0 : return;
407 : }
408 :
409 15 : const JSStringFinalizer* fin = externalFinalizer();
410 15 : fin->finalize(fin, const_cast<char16_t*>(rawTwoByteChars()));
411 : }
412 :
413 : #endif /* vm_String_inl_h */
|