1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=79 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey JavaScript engine.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 2009
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Luke Wagner <luke@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef String_inl_h__
42 : #define String_inl_h__
43 :
44 : #include "jscntxt.h"
45 : #include "jsgcmark.h"
46 : #include "jsprobes.h"
47 :
48 : #include "String.h"
49 :
50 : #include "jsgcinlines.h"
51 :
52 : inline void
53 12585302 : JSString::writeBarrierPre(JSString *str)
54 : {
55 : #ifdef JSGC_INCREMENTAL
56 12585302 : if (!str)
57 240723 : return;
58 :
59 12344579 : JSCompartment *comp = str->compartment();
60 12344579 : if (comp->needsBarrier()) {
61 203 : JSString *tmp = str;
62 203 : MarkStringUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
63 203 : JS_ASSERT(tmp == str);
64 : }
65 : #endif
66 : }
67 :
68 : inline void
69 127778730 : JSString::writeBarrierPost(JSString *str, void *addr)
70 : {
71 127778730 : }
72 :
73 : inline bool
74 2566819 : JSString::needWriteBarrierPre(JSCompartment *comp)
75 : {
76 : #ifdef JSGC_INCREMENTAL
77 2566819 : return comp->needsBarrier();
78 : #else
79 : return false;
80 : #endif
81 : }
82 :
83 : inline void
84 255040905 : JSString::readBarrier(JSString *str)
85 : {
86 : #ifdef JSGC_INCREMENTAL
87 255040905 : JSCompartment *comp = str->compartment();
88 255040905 : if (comp->needsBarrier()) {
89 138496 : JSString *tmp = str;
90 138496 : MarkStringUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
91 138496 : JS_ASSERT(tmp == str);
92 : }
93 : #endif
94 255040905 : }
95 :
96 : JS_ALWAYS_INLINE bool
97 139646656 : JSString::validateLength(JSContext *cx, size_t length)
98 : {
99 139646656 : if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
100 18 : js_ReportAllocationOverflow(cx);
101 18 : return false;
102 : }
103 :
104 139646638 : return true;
105 : }
106 :
107 : JS_ALWAYS_INLINE void
108 21991264 : JSRope::init(JSString *left, JSString *right, size_t length)
109 : {
110 21991264 : d.lengthAndFlags = buildLengthAndFlags(length, ROPE_BIT);
111 21991264 : d.u1.left = left;
112 21991264 : d.s.u2.right = right;
113 21991264 : JSString::writeBarrierPost(d.u1.left, &d.u1.left);
114 21991264 : JSString::writeBarrierPost(d.s.u2.right, &d.s.u2.right);
115 21991264 : }
116 :
117 : JS_ALWAYS_INLINE JSRope *
118 21991264 : JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length)
119 : {
120 21991264 : if (!validateLength(cx, length))
121 0 : return NULL;
122 21991264 : JSRope *str = (JSRope *)js_NewGCString(cx);
123 21991264 : if (!str)
124 0 : return NULL;
125 21991264 : str->init(left, right, length);
126 21991264 : return str;
127 : }
128 :
129 : inline void
130 2154 : JSRope::markChildren(JSTracer *trc)
131 : {
132 2154 : js::gc::MarkStringUnbarriered(trc, &d.u1.left, "left child");
133 2154 : js::gc::MarkStringUnbarriered(trc, &d.s.u2.right, "right child");
134 2154 : }
135 :
136 : JS_ALWAYS_INLINE void
137 1001050 : JSDependentString::init(JSLinearString *base, const jschar *chars, size_t length)
138 : {
139 1001050 : d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_BIT);
140 1001050 : d.u1.chars = chars;
141 1001050 : d.s.u2.base = base;
142 1001050 : JSString::writeBarrierPost(d.s.u2.base, &d.s.u2.base);
143 1001050 : }
144 :
145 : JS_ALWAYS_INLINE JSDependentString *
146 1001050 : JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars, size_t length)
147 : {
148 : /* Try to avoid long chains of dependent strings. */
149 2036998 : while (base->isDependent())
150 34898 : base = base->asDependent().base();
151 :
152 1001050 : JS_ASSERT(base->isFlat());
153 1001050 : JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length());
154 1001050 : JS_ASSERT(length <= base->length() - (chars - base->chars()));
155 :
156 1001050 : JSDependentString *str = (JSDependentString *)js_NewGCString(cx);
157 1001050 : if (!str)
158 0 : return NULL;
159 1001050 : str->init(base, chars, length);
160 1001050 : return str;
161 : }
162 :
163 : inline void
164 1019 : JSDependentString::markChildren(JSTracer *trc)
165 : {
166 1019 : js::gc::MarkStringUnbarriered(trc, &d.s.u2.base, "base");
167 1019 : }
168 :
169 : inline js::PropertyName *
170 2 : JSFlatString::toPropertyName(JSContext *cx)
171 : {
172 : #ifdef DEBUG
173 : uint32_t dummy;
174 2 : JS_ASSERT(!isIndex(&dummy));
175 : #endif
176 2 : if (isAtom())
177 0 : return asAtom().asPropertyName();
178 2 : JSAtom *atom = js_AtomizeString(cx, this);
179 2 : if (!atom)
180 0 : return NULL;
181 2 : return atom->asPropertyName();
182 : }
183 :
184 : JS_ALWAYS_INLINE void
185 14485956 : JSFixedString::init(const jschar *chars, size_t length)
186 : {
187 14485956 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
188 14485956 : d.u1.chars = chars;
189 14485956 : }
190 :
191 : JS_ALWAYS_INLINE JSFixedString *
192 14485956 : JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length)
193 : {
194 14485956 : JS_ASSERT(chars[length] == jschar(0));
195 :
196 14485956 : if (!validateLength(cx, length))
197 0 : return NULL;
198 14485956 : JSFixedString *str = (JSFixedString *)js_NewGCString(cx);
199 14485956 : if (!str)
200 0 : return NULL;
201 14485956 : str->init(chars, length);
202 14485956 : return str;
203 : }
204 :
205 : JS_ALWAYS_INLINE JSAtom *
206 108791374 : JSFixedString::morphAtomizedStringIntoAtom()
207 : {
208 108791374 : JS_ASSERT((d.lengthAndFlags & FLAGS_MASK) == JS_BIT(2));
209 : JS_STATIC_ASSERT(NON_STATIC_ATOM == JS_BIT(3));
210 108791374 : d.lengthAndFlags ^= (JS_BIT(2) | JS_BIT(3));
211 108791374 : return &asAtom();
212 : }
213 :
214 : JS_ALWAYS_INLINE JSInlineString *
215 91557955 : JSInlineString::new_(JSContext *cx)
216 : {
217 91557955 : return (JSInlineString *)js_NewGCString(cx);
218 : }
219 :
220 : JS_ALWAYS_INLINE jschar *
221 101886784 : JSInlineString::init(size_t length)
222 : {
223 101886784 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
224 101886784 : d.u1.chars = d.inlineStorage;
225 101886784 : JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
226 101886783 : return d.inlineStorage;
227 : }
228 :
229 : JS_ALWAYS_INLINE void
230 0 : JSInlineString::resetLength(size_t length)
231 : {
232 0 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
233 0 : JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
234 0 : }
235 :
236 : JS_ALWAYS_INLINE JSShortString *
237 8353038 : JSShortString::new_(JSContext *cx)
238 : {
239 8353038 : return js_NewGCShortString(cx);
240 : }
241 :
242 : JS_ALWAYS_INLINE void
243 42428174 : JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length)
244 : {
245 42428174 : JS_ASSERT(lengthFits(length + (chars - d.inlineStorage)));
246 42428174 : JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH);
247 42428174 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
248 42428174 : d.u1.chars = chars;
249 42428174 : }
250 :
251 : JS_ALWAYS_INLINE void
252 977008 : JSExternalString::init(const jschar *chars, size_t length, const JSStringFinalizer *fin)
253 : {
254 977008 : JS_ASSERT(fin);
255 977008 : JS_ASSERT(fin->finalize);
256 977008 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
257 977008 : d.u1.chars = chars;
258 977008 : d.s.u2.externalFinalizer = fin;
259 977008 : }
260 :
261 : JS_ALWAYS_INLINE JSExternalString *
262 977008 : JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length,
263 : const JSStringFinalizer *fin)
264 : {
265 977008 : JS_ASSERT(chars[length] == 0);
266 :
267 977008 : if (!validateLength(cx, length))
268 0 : return NULL;
269 977008 : JSExternalString *str = js_NewGCExternalString(cx);
270 977008 : if (!str)
271 0 : return NULL;
272 977008 : str->init(chars, length, fin);
273 977008 : cx->runtime->updateMallocCounter(cx, (length + 1) * sizeof(jschar));
274 977008 : return str;
275 : }
276 :
277 : inline bool
278 7793366 : js::StaticStrings::fitsInSmallChar(jschar c)
279 : {
280 7793366 : return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
281 : }
282 :
283 : inline bool
284 7050822 : js::StaticStrings::hasUnit(jschar c)
285 : {
286 7050822 : return c < UNIT_STATIC_LIMIT;
287 : }
288 :
289 : inline JSAtom *
290 5112297 : js::StaticStrings::getUnit(jschar c)
291 : {
292 5112297 : JS_ASSERT(hasUnit(c));
293 5112297 : return unitStaticTable[c];
294 : }
295 :
296 : inline bool
297 3716504 : js::StaticStrings::hasUint(uint32_t u)
298 : {
299 3716504 : return u < INT_STATIC_LIMIT;
300 : }
301 :
302 : inline JSAtom *
303 3715810 : js::StaticStrings::getUint(uint32_t u)
304 : {
305 3715810 : JS_ASSERT(hasUint(u));
306 3715810 : return intStaticTable[u];
307 : }
308 :
309 : inline bool
310 49872253 : js::StaticStrings::hasInt(int32_t i)
311 : {
312 49872253 : return uint32_t(i) < INT_STATIC_LIMIT;
313 : }
314 :
315 : inline JSAtom *
316 3715172 : js::StaticStrings::getInt(int32_t i)
317 : {
318 3715172 : JS_ASSERT(hasInt(i));
319 3715172 : return getUint(uint32_t(i));
320 : }
321 :
322 : inline JSLinearString *
323 122297 : js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
324 : {
325 122297 : JS_ASSERT(index < str->length());
326 122297 : const jschar *chars = str->getChars(cx);
327 122297 : if (!chars)
328 0 : return NULL;
329 122297 : jschar c = chars[index];
330 122297 : if (c < UNIT_STATIC_LIMIT)
331 116021 : return getUnit(c);
332 6276 : return js_NewDependentString(cx, str, index, 1);
333 : }
334 :
335 : inline JSAtom *
336 1881134 : js::StaticStrings::getLength2(jschar c1, jschar c2)
337 : {
338 1881134 : JS_ASSERT(fitsInSmallChar(c1));
339 1881134 : JS_ASSERT(fitsInSmallChar(c2));
340 1881134 : size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2];
341 1881134 : return length2StaticTable[index];
342 : }
343 :
344 : inline JSAtom *
345 : js::StaticStrings::getLength2(uint32_t i)
346 : {
347 : JS_ASSERT(i < 100);
348 : return getLength2('0' + i / 10, '0' + i % 10);
349 : }
350 :
351 : /* Get a static atomized string for chars if possible. */
352 : inline JSAtom *
353 79591296 : js::StaticStrings::lookup(const jschar *chars, size_t length)
354 : {
355 79591296 : switch (length) {
356 : case 1:
357 4822304 : if (chars[0] < UNIT_STATIC_LIMIT)
358 4234444 : return getUnit(chars[0]);
359 587860 : return NULL;
360 : case 2:
361 2134578 : if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
362 1881134 : return getLength2(chars[0], chars[1]);
363 253444 : return NULL;
364 : case 3:
365 : /*
366 : * Here we know that JSString::intStringTable covers only 256 (or at least
367 : * not 1000 or more) chars. We rely on order here to resolve the unit vs.
368 : * int string/length-2 string atom identity issue by giving priority to unit
369 : * strings for "0" through "9" and length-2 strings for "10" through "99".
370 : */
371 : JS_STATIC_ASSERT(INT_STATIC_LIMIT <= 999);
372 13700782 : if ('1' <= chars[0] && chars[0] <= '9' &&
373 2833302 : '0' <= chars[1] && chars[1] <= '9' &&
374 2823764 : '0' <= chars[2] && chars[2] <= '9') {
375 1411813 : int i = (chars[0] - '0') * 100 +
376 1411813 : (chars[1] - '0') * 10 +
377 2823626 : (chars[2] - '0');
378 :
379 1411813 : if (unsigned(i) < INT_STATIC_LIMIT)
380 2179 : return getInt(i);
381 : }
382 8041537 : return NULL;
383 : }
384 :
385 64590698 : return NULL;
386 : }
387 :
388 : JS_ALWAYS_INLINE void
389 38030755 : JSString::finalize(JSContext *cx, bool background)
390 : {
391 : /* Shorts are in a different arena. */
392 38030755 : JS_ASSERT(!isShort());
393 :
394 38030755 : if (isFlat())
395 16091603 : asFlat().finalize(cx->runtime);
396 : else
397 21939152 : JS_ASSERT(isDependent() || isRope());
398 38030755 : }
399 :
400 : inline void
401 17341424 : JSFlatString::finalize(JSRuntime *rt)
402 : {
403 17341424 : JS_ASSERT(!isShort());
404 :
405 : /*
406 : * This check depends on the fact that 'chars' is only initialized to the
407 : * beginning of inlineStorage. E.g., this is not the case for short strings.
408 : */
409 17341424 : if (chars() != d.inlineStorage)
410 15537954 : rt->free_(const_cast<jschar *>(chars()));
411 17341424 : }
412 :
413 : inline void
414 50347824 : JSShortString::finalize(JSContext *cx, bool background)
415 : {
416 50347824 : JS_ASSERT(JSString::isShort());
417 50347824 : }
418 :
419 : inline void
420 3658269 : JSAtom::finalize(JSRuntime *rt)
421 : {
422 3658269 : JS_ASSERT(JSString::isAtom());
423 3658269 : if (getAllocKind() == js::gc::FINALIZE_STRING)
424 1249821 : JSFlatString::finalize(rt);
425 : else
426 2408448 : JS_ASSERT(getAllocKind() == js::gc::FINALIZE_SHORT_STRING);
427 3658269 : }
428 :
429 : inline void
430 977008 : JSExternalString::finalize(JSContext *cx, bool background)
431 : {
432 977008 : finalize();
433 977008 : }
434 :
435 : inline void
436 977008 : JSExternalString::finalize()
437 : {
438 977008 : const JSStringFinalizer *fin = externalFinalizer();
439 977008 : fin->finalize(fin, const_cast<jschar *>(chars()));
440 977008 : }
441 :
442 : namespace js {
443 :
444 : static JS_ALWAYS_INLINE JSFixedString *
445 99764564 : NewShortString(JSContext *cx, const jschar *chars, size_t length)
446 : {
447 : /*
448 : * Don't bother trying to find a static atom; measurement shows that not
449 : * many get here (for one, Atomize is catching them).
450 : */
451 99764564 : JS_ASSERT(JSShortString::lengthFits(length));
452 99764564 : JSInlineString *str = JSInlineString::lengthFits(length)
453 : ? JSInlineString::new_(cx)
454 99764564 : : JSShortString::new_(cx);
455 99764564 : if (!str)
456 0 : return NULL;
457 :
458 99764564 : jschar *storage = str->init(length);
459 99764564 : PodCopy(storage, chars, length);
460 99764564 : storage[length] = 0;
461 99764564 : Probes::createString(cx, str, length);
462 99764564 : return str;
463 : }
464 :
465 : } /* namespace js */
466 :
467 : #endif
|