|
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 |
|
14 #include "jscntxt.h" |
|
15 |
|
16 #include "gc/Marking.h" |
|
17 |
|
18 #include "jsgcinlines.h" |
|
19 |
|
20 namespace js { |
|
21 |
|
22 template <AllowGC allowGC> |
|
23 static MOZ_ALWAYS_INLINE JSInlineString * |
|
24 NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars) |
|
25 { |
|
26 size_t len = chars.length(); |
|
27 JS_ASSERT(JSFatInlineString::lengthFits(len)); |
|
28 JSInlineString *str = JSInlineString::lengthFits(len) |
|
29 ? JSInlineString::new_<allowGC>(cx) |
|
30 : JSFatInlineString::new_<allowGC>(cx); |
|
31 if (!str) |
|
32 return nullptr; |
|
33 |
|
34 jschar *p = str->init(len); |
|
35 for (size_t i = 0; i < len; ++i) |
|
36 p[i] = static_cast<jschar>(chars[i]); |
|
37 p[len] = '\0'; |
|
38 return str; |
|
39 } |
|
40 |
|
41 template <AllowGC allowGC> |
|
42 static MOZ_ALWAYS_INLINE JSInlineString * |
|
43 NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars) |
|
44 { |
|
45 size_t len = chars.length(); |
|
46 |
|
47 /* |
|
48 * Don't bother trying to find a static atom; measurement shows that not |
|
49 * many get here (for one, Atomize is catching them). |
|
50 */ |
|
51 JS_ASSERT(JSFatInlineString::lengthFits(len)); |
|
52 JSInlineString *str = JSInlineString::lengthFits(len) |
|
53 ? JSInlineString::new_<allowGC>(cx) |
|
54 : JSFatInlineString::new_<allowGC>(cx); |
|
55 if (!str) |
|
56 return nullptr; |
|
57 |
|
58 jschar *storage = str->init(len); |
|
59 mozilla::PodCopy(storage, chars.start().get(), len); |
|
60 storage[len] = 0; |
|
61 return str; |
|
62 } |
|
63 |
|
64 static inline void |
|
65 StringWriteBarrierPost(js::ThreadSafeContext *maybecx, JSString **strp) |
|
66 { |
|
67 } |
|
68 |
|
69 static inline void |
|
70 StringWriteBarrierPostRemove(js::ThreadSafeContext *maybecx, JSString **strp) |
|
71 { |
|
72 } |
|
73 |
|
74 } /* namespace js */ |
|
75 |
|
76 MOZ_ALWAYS_INLINE bool |
|
77 JSString::validateLength(js::ThreadSafeContext *maybecx, size_t length) |
|
78 { |
|
79 if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) { |
|
80 js_ReportAllocationOverflow(maybecx); |
|
81 return false; |
|
82 } |
|
83 |
|
84 return true; |
|
85 } |
|
86 |
|
87 MOZ_ALWAYS_INLINE void |
|
88 JSRope::init(js::ThreadSafeContext *cx, JSString *left, JSString *right, size_t length) |
|
89 { |
|
90 d.lengthAndFlags = buildLengthAndFlags(length, ROPE_FLAGS); |
|
91 d.u1.left = left; |
|
92 d.s.u2.right = right; |
|
93 js::StringWriteBarrierPost(cx, &d.u1.left); |
|
94 js::StringWriteBarrierPost(cx, &d.s.u2.right); |
|
95 } |
|
96 |
|
97 template <js::AllowGC allowGC> |
|
98 MOZ_ALWAYS_INLINE JSRope * |
|
99 JSRope::new_(js::ThreadSafeContext *cx, |
|
100 typename js::MaybeRooted<JSString*, allowGC>::HandleType left, |
|
101 typename js::MaybeRooted<JSString*, allowGC>::HandleType right, |
|
102 size_t length) |
|
103 { |
|
104 if (!validateLength(cx, length)) |
|
105 return nullptr; |
|
106 JSRope *str = (JSRope *) js_NewGCString<allowGC>(cx); |
|
107 if (!str) |
|
108 return nullptr; |
|
109 str->init(cx, left, right, length); |
|
110 return str; |
|
111 } |
|
112 |
|
113 inline void |
|
114 JSRope::markChildren(JSTracer *trc) |
|
115 { |
|
116 js::gc::MarkStringUnbarriered(trc, &d.u1.left, "left child"); |
|
117 js::gc::MarkStringUnbarriered(trc, &d.s.u2.right, "right child"); |
|
118 } |
|
119 |
|
120 MOZ_ALWAYS_INLINE void |
|
121 JSDependentString::init(js::ThreadSafeContext *cx, JSLinearString *base, const jschar *chars, |
|
122 size_t length) |
|
123 { |
|
124 JS_ASSERT(!js::IsPoisonedPtr(base)); |
|
125 d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_FLAGS); |
|
126 d.u1.chars = chars; |
|
127 d.s.u2.base = base; |
|
128 js::StringWriteBarrierPost(cx, reinterpret_cast<JSString **>(&d.s.u2.base)); |
|
129 } |
|
130 |
|
131 MOZ_ALWAYS_INLINE JSLinearString * |
|
132 JSDependentString::new_(js::ExclusiveContext *cx, |
|
133 JSLinearString *baseArg, const jschar *chars, size_t length) |
|
134 { |
|
135 /* Try to avoid long chains of dependent strings. */ |
|
136 while (baseArg->isDependent()) |
|
137 baseArg = baseArg->asDependent().base(); |
|
138 |
|
139 JS_ASSERT(baseArg->isFlat()); |
|
140 |
|
141 /* |
|
142 * The chars we are pointing into must be owned by something in the chain |
|
143 * of dependent or undepended strings kept alive by our base pointer. |
|
144 */ |
|
145 #ifdef DEBUG |
|
146 for (JSLinearString *b = baseArg; ; b = b->base()) { |
|
147 if (chars >= b->chars() && chars < b->chars() + b->length() && |
|
148 length <= b->length() - (chars - b->chars())) |
|
149 { |
|
150 break; |
|
151 } |
|
152 } |
|
153 #endif |
|
154 |
|
155 /* |
|
156 * Do not create a string dependent on inline chars from another string, |
|
157 * both to avoid the awkward moving-GC hazard this introduces and because it |
|
158 * is more efficient to immediately undepend here. |
|
159 */ |
|
160 if (JSFatInlineString::lengthFits(length)) |
|
161 return js::NewFatInlineString<js::CanGC>(cx, JS::TwoByteChars(chars, length)); |
|
162 |
|
163 JSDependentString *str = (JSDependentString *)js_NewGCString<js::NoGC>(cx); |
|
164 if (str) { |
|
165 str->init(cx, baseArg, chars, length); |
|
166 return str; |
|
167 } |
|
168 |
|
169 JS::Rooted<JSLinearString*> base(cx, baseArg); |
|
170 |
|
171 str = (JSDependentString *)js_NewGCString<js::CanGC>(cx); |
|
172 if (!str) |
|
173 return nullptr; |
|
174 str->init(cx, base, chars, length); |
|
175 return str; |
|
176 } |
|
177 |
|
178 inline void |
|
179 JSString::markBase(JSTracer *trc) |
|
180 { |
|
181 JS_ASSERT(hasBase()); |
|
182 js::gc::MarkStringUnbarriered(trc, &d.s.u2.base, "base"); |
|
183 } |
|
184 |
|
185 MOZ_ALWAYS_INLINE void |
|
186 JSFlatString::init(const jschar *chars, size_t length) |
|
187 { |
|
188 d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); |
|
189 d.u1.chars = chars; |
|
190 } |
|
191 |
|
192 template <js::AllowGC allowGC> |
|
193 MOZ_ALWAYS_INLINE JSFlatString * |
|
194 JSFlatString::new_(js::ThreadSafeContext *cx, const jschar *chars, size_t length) |
|
195 { |
|
196 JS_ASSERT(chars[length] == jschar(0)); |
|
197 |
|
198 if (!validateLength(cx, length)) |
|
199 return nullptr; |
|
200 JSFlatString *str = (JSFlatString *)js_NewGCString<allowGC>(cx); |
|
201 if (!str) |
|
202 return nullptr; |
|
203 str->init(chars, length); |
|
204 return str; |
|
205 } |
|
206 |
|
207 inline js::PropertyName * |
|
208 JSFlatString::toPropertyName(JSContext *cx) |
|
209 { |
|
210 #ifdef DEBUG |
|
211 uint32_t dummy; |
|
212 JS_ASSERT(!isIndex(&dummy)); |
|
213 #endif |
|
214 if (isAtom()) |
|
215 return asAtom().asPropertyName(); |
|
216 JSAtom *atom = js::AtomizeString(cx, this); |
|
217 if (!atom) |
|
218 return nullptr; |
|
219 return atom->asPropertyName(); |
|
220 } |
|
221 |
|
222 template <js::AllowGC allowGC> |
|
223 MOZ_ALWAYS_INLINE JSInlineString * |
|
224 JSInlineString::new_(js::ThreadSafeContext *cx) |
|
225 { |
|
226 return (JSInlineString *)js_NewGCString<allowGC>(cx); |
|
227 } |
|
228 |
|
229 MOZ_ALWAYS_INLINE jschar * |
|
230 JSInlineString::init(size_t length) |
|
231 { |
|
232 d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); |
|
233 d.u1.chars = d.inlineStorage; |
|
234 JS_ASSERT(lengthFits(length) || (isFatInline() && JSFatInlineString::lengthFits(length))); |
|
235 return d.inlineStorage; |
|
236 } |
|
237 |
|
238 MOZ_ALWAYS_INLINE void |
|
239 JSInlineString::resetLength(size_t length) |
|
240 { |
|
241 d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); |
|
242 JS_ASSERT(lengthFits(length) || (isFatInline() && JSFatInlineString::lengthFits(length))); |
|
243 } |
|
244 |
|
245 template <js::AllowGC allowGC> |
|
246 MOZ_ALWAYS_INLINE JSFatInlineString * |
|
247 JSFatInlineString::new_(js::ThreadSafeContext *cx) |
|
248 { |
|
249 return js_NewGCFatInlineString<allowGC>(cx); |
|
250 } |
|
251 |
|
252 MOZ_ALWAYS_INLINE void |
|
253 JSExternalString::init(const jschar *chars, size_t length, const JSStringFinalizer *fin) |
|
254 { |
|
255 JS_ASSERT(fin); |
|
256 JS_ASSERT(fin->finalize); |
|
257 d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS); |
|
258 d.u1.chars = chars; |
|
259 d.s.u2.externalFinalizer = fin; |
|
260 } |
|
261 |
|
262 MOZ_ALWAYS_INLINE JSExternalString * |
|
263 JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, |
|
264 const JSStringFinalizer *fin) |
|
265 { |
|
266 JS_ASSERT(chars[length] == 0); |
|
267 |
|
268 if (!validateLength(cx, length)) |
|
269 return nullptr; |
|
270 JSExternalString *str = js_NewGCExternalString(cx); |
|
271 if (!str) |
|
272 return nullptr; |
|
273 str->init(chars, length, fin); |
|
274 cx->runtime()->updateMallocCounter(cx->zone(), (length + 1) * sizeof(jschar)); |
|
275 return str; |
|
276 } |
|
277 |
|
278 inline JSLinearString * |
|
279 js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index) |
|
280 { |
|
281 JS_ASSERT(index < str->length()); |
|
282 |
|
283 jschar c; |
|
284 if (!str->getChar(cx, index, &c)) |
|
285 return nullptr; |
|
286 if (c < UNIT_STATIC_LIMIT) |
|
287 return getUnit(c); |
|
288 return js_NewDependentString(cx, str, index, 1); |
|
289 } |
|
290 |
|
291 inline JSAtom * |
|
292 js::StaticStrings::getLength2(jschar c1, jschar c2) |
|
293 { |
|
294 JS_ASSERT(fitsInSmallChar(c1)); |
|
295 JS_ASSERT(fitsInSmallChar(c2)); |
|
296 size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]; |
|
297 return length2StaticTable[index]; |
|
298 } |
|
299 |
|
300 MOZ_ALWAYS_INLINE void |
|
301 JSString::finalize(js::FreeOp *fop) |
|
302 { |
|
303 /* FatInline strings are in a different arena. */ |
|
304 JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING); |
|
305 |
|
306 if (isFlat()) |
|
307 asFlat().finalize(fop); |
|
308 else |
|
309 JS_ASSERT(isDependent() || isRope()); |
|
310 } |
|
311 |
|
312 inline void |
|
313 JSFlatString::finalize(js::FreeOp *fop) |
|
314 { |
|
315 JS_ASSERT(getAllocKind() != js::gc::FINALIZE_FAT_INLINE_STRING); |
|
316 |
|
317 if (chars() != d.inlineStorage) |
|
318 fop->free_(const_cast<jschar *>(chars())); |
|
319 } |
|
320 |
|
321 inline void |
|
322 JSFatInlineString::finalize(js::FreeOp *fop) |
|
323 { |
|
324 JS_ASSERT(getAllocKind() == js::gc::FINALIZE_FAT_INLINE_STRING); |
|
325 |
|
326 if (chars() != d.inlineStorage) |
|
327 fop->free_(const_cast<jschar *>(chars())); |
|
328 } |
|
329 |
|
330 inline void |
|
331 JSAtom::finalize(js::FreeOp *fop) |
|
332 { |
|
333 JS_ASSERT(JSString::isAtom()); |
|
334 JS_ASSERT(JSString::isFlat()); |
|
335 |
|
336 if (chars() != d.inlineStorage) |
|
337 fop->free_(const_cast<jschar *>(chars())); |
|
338 } |
|
339 |
|
340 inline void |
|
341 JSExternalString::finalize(js::FreeOp *fop) |
|
342 { |
|
343 const JSStringFinalizer *fin = externalFinalizer(); |
|
344 fin->finalize(fin, const_cast<jschar *>(chars())); |
|
345 } |
|
346 |
|
347 #endif /* vm_String_inl_h */ |