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 : #include "jsobjinlines.h"
52 : #include "gc/Barrier-inl.h"
53 :
54 : inline void
55 2511006 : JSString::writeBarrierPre(JSString *str)
56 : {
57 : #ifdef JSGC_INCREMENTAL
58 2511006 : if (!str)
59 149230 : return;
60 :
61 2361776 : JSCompartment *comp = str->compartment();
62 2361776 : if (comp->needsBarrier()) {
63 77 : JSString *tmp = str;
64 77 : MarkStringUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
65 77 : JS_ASSERT(tmp == str);
66 : }
67 : #endif
68 : }
69 :
70 : inline void
71 120765240 : JSString::writeBarrierPost(JSString *str, void *addr)
72 : {
73 120765240 : }
74 :
75 : inline bool
76 2253934 : JSString::needWriteBarrierPre(JSCompartment *comp)
77 : {
78 : #ifdef JSGC_INCREMENTAL
79 2253934 : return comp->needsBarrier();
80 : #else
81 : return false;
82 : #endif
83 : }
84 :
85 : inline void
86 88295600 : JSString::readBarrier(JSString *str)
87 : {
88 : #ifdef JSGC_INCREMENTAL
89 88295600 : JSCompartment *comp = str->compartment();
90 88295600 : if (comp->needsBarrier()) {
91 128068 : JSString *tmp = str;
92 128068 : MarkStringUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
93 128068 : JS_ASSERT(tmp == str);
94 : }
95 : #endif
96 88295600 : }
97 :
98 : JS_ALWAYS_INLINE bool
99 79143659 : JSString::validateLength(JSContext *cx, size_t length)
100 : {
101 79143659 : if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
102 18 : js_ReportAllocationOverflow(cx);
103 18 : return false;
104 : }
105 :
106 79143641 : return true;
107 : }
108 :
109 : JS_ALWAYS_INLINE void
110 18638421 : JSRope::init(JSString *left, JSString *right, size_t length)
111 : {
112 18638421 : d.lengthAndFlags = buildLengthAndFlags(length, ROPE_BIT);
113 18638421 : d.u1.left = left;
114 18638421 : d.s.u2.right = right;
115 18638421 : JSString::writeBarrierPost(d.u1.left, &d.u1.left);
116 18638421 : JSString::writeBarrierPost(d.s.u2.right, &d.s.u2.right);
117 18638421 : }
118 :
119 : JS_ALWAYS_INLINE JSRope *
120 18638421 : JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length)
121 : {
122 18638421 : if (!validateLength(cx, length))
123 0 : return NULL;
124 18638421 : JSRope *str = (JSRope *)js_NewGCString(cx);
125 18638421 : if (!str)
126 0 : return NULL;
127 18638421 : str->init(left, right, length);
128 18638421 : return str;
129 : }
130 :
131 : inline void
132 2172 : JSRope::markChildren(JSTracer *trc)
133 : {
134 2172 : js::gc::MarkStringUnbarriered(trc, &d.u1.left, "left child");
135 2172 : js::gc::MarkStringUnbarriered(trc, &d.s.u2.right, "right child");
136 2172 : }
137 :
138 : JS_ALWAYS_INLINE void
139 863144 : JSDependentString::init(JSLinearString *base, const jschar *chars, size_t length)
140 : {
141 863144 : d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_BIT);
142 863144 : d.u1.chars = chars;
143 863144 : d.s.u2.base = base;
144 863144 : JSString::writeBarrierPost(d.s.u2.base, &d.s.u2.base);
145 863144 : }
146 :
147 : JS_ALWAYS_INLINE JSDependentString *
148 863144 : JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars, size_t length)
149 : {
150 : /* Try to avoid long chains of dependent strings. */
151 1749571 : while (base->isDependent())
152 23283 : base = base->asDependent().base();
153 :
154 863144 : JS_ASSERT(base->isFlat());
155 863144 : JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length());
156 863144 : JS_ASSERT(length <= base->length() - (chars - base->chars()));
157 :
158 863144 : JSDependentString *str = (JSDependentString *)js_NewGCString(cx);
159 863144 : if (!str)
160 0 : return NULL;
161 863144 : str->init(base, chars, length);
162 863144 : return str;
163 : }
164 :
165 : inline void
166 1000 : JSDependentString::markChildren(JSTracer *trc)
167 : {
168 1000 : js::gc::MarkStringUnbarriered(trc, &d.s.u2.base, "base");
169 1000 : }
170 :
171 : inline js::PropertyName *
172 2 : JSFlatString::toPropertyName(JSContext *cx)
173 : {
174 : #ifdef DEBUG
175 : uint32_t dummy;
176 2 : JS_ASSERT(!isIndex(&dummy));
177 : #endif
178 2 : if (isAtom())
179 0 : return asAtom().asPropertyName();
180 2 : JSAtom *atom = js_AtomizeString(cx, this);
181 2 : if (!atom)
182 0 : return NULL;
183 2 : return atom->asPropertyName();
184 : }
185 :
186 : JS_ALWAYS_INLINE void
187 6777523 : JSFixedString::init(const jschar *chars, size_t length)
188 : {
189 6777523 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
190 6777523 : d.u1.chars = chars;
191 6777523 : }
192 :
193 : JS_ALWAYS_INLINE JSFixedString *
194 6777523 : JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length)
195 : {
196 6777523 : JS_ASSERT(chars[length] == jschar(0));
197 :
198 6777523 : if (!validateLength(cx, length))
199 0 : return NULL;
200 6777523 : JSFixedString *str = (JSFixedString *)js_NewGCString(cx);
201 6777523 : if (!str)
202 0 : return NULL;
203 6777523 : str->init(chars, length);
204 6777523 : return str;
205 : }
206 :
207 : JS_ALWAYS_INLINE JSAtom *
208 96439813 : JSFixedString::morphAtomizedStringIntoAtom()
209 : {
210 96439813 : JS_ASSERT((d.lengthAndFlags & FLAGS_MASK) == JS_BIT(2));
211 : JS_STATIC_ASSERT(NON_STATIC_ATOM == JS_BIT(3));
212 96439813 : d.lengthAndFlags ^= (JS_BIT(2) | JS_BIT(3));
213 96439813 : return &asAtom();
214 : }
215 :
216 : JS_ALWAYS_INLINE JSInlineString *
217 85752816 : JSInlineString::new_(JSContext *cx)
218 : {
219 85752816 : return (JSInlineString *)js_NewGCString(cx);
220 : }
221 :
222 : JS_ALWAYS_INLINE jschar *
223 93260425 : JSInlineString::init(size_t length)
224 : {
225 93260425 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
226 93260425 : d.u1.chars = d.inlineStorage;
227 93260425 : JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
228 93260425 : return d.inlineStorage;
229 : }
230 :
231 : JS_ALWAYS_INLINE void
232 0 : JSInlineString::resetLength(size_t length)
233 : {
234 0 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
235 0 : JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
236 0 : }
237 :
238 : JS_ALWAYS_INLINE JSShortString *
239 6369198 : JSShortString::new_(JSContext *cx)
240 : {
241 6369198 : return js_NewGCShortString(cx);
242 : }
243 :
244 : JS_ALWAYS_INLINE void
245 38815626 : JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length)
246 : {
247 38815626 : JS_ASSERT(lengthFits(length + (chars - d.inlineStorage)));
248 38815626 : JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH);
249 38815626 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
250 38815626 : d.u1.chars = chars;
251 38815626 : }
252 :
253 : JS_ALWAYS_INLINE void
254 2000 : JSExternalString::init(const jschar *chars, size_t length, const JSStringFinalizer *fin)
255 : {
256 2000 : JS_ASSERT(fin);
257 2000 : JS_ASSERT(fin->finalize);
258 2000 : d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
259 2000 : d.u1.chars = chars;
260 2000 : d.s.u2.externalFinalizer = fin;
261 2000 : }
262 :
263 : JS_ALWAYS_INLINE JSExternalString *
264 2000 : JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length,
265 : const JSStringFinalizer *fin)
266 : {
267 2000 : JS_ASSERT(chars[length] == 0);
268 :
269 2000 : if (!validateLength(cx, length))
270 0 : return NULL;
271 2000 : JSExternalString *str = js_NewGCExternalString(cx);
272 2000 : if (!str)
273 0 : return NULL;
274 2000 : str->init(chars, length, fin);
275 2000 : cx->runtime->updateMallocCounter(cx, (length + 1) * sizeof(jschar));
276 2000 : return str;
277 : }
278 :
279 : inline bool
280 2840412 : js::StaticStrings::fitsInSmallChar(jschar c)
281 : {
282 2840412 : return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
283 : }
284 :
285 : inline bool
286 4387595 : js::StaticStrings::hasUnit(jschar c)
287 : {
288 4387595 : return c < UNIT_STATIC_LIMIT;
289 : }
290 :
291 : inline JSAtom *
292 3080504 : js::StaticStrings::getUnit(jschar c)
293 : {
294 3080504 : JS_ASSERT(hasUnit(c));
295 3080504 : return unitStaticTable[c];
296 : }
297 :
298 : inline bool
299 909870 : js::StaticStrings::hasUint(uint32_t u)
300 : {
301 909870 : return u < INT_STATIC_LIMIT;
302 : }
303 :
304 : inline JSAtom *
305 909792 : js::StaticStrings::getUint(uint32_t u)
306 : {
307 909792 : JS_ASSERT(hasUint(u));
308 909792 : return intStaticTable[u];
309 : }
310 :
311 : inline bool
312 40648689 : js::StaticStrings::hasInt(int32_t i)
313 : {
314 40648689 : return uint32_t(i) < INT_STATIC_LIMIT;
315 : }
316 :
317 : inline JSAtom *
318 909770 : js::StaticStrings::getInt(int32_t i)
319 : {
320 909770 : JS_ASSERT(hasInt(i));
321 909770 : return getUint(uint32_t(i));
322 : }
323 :
324 : inline JSLinearString *
325 7731 : js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
326 : {
327 7731 : JS_ASSERT(index < str->length());
328 7731 : const jschar *chars = str->getChars(cx);
329 7731 : if (!chars)
330 0 : return NULL;
331 7731 : jschar c = chars[index];
332 7731 : if (c < UNIT_STATIC_LIMIT)
333 7722 : return getUnit(c);
334 9 : return js_NewDependentString(cx, str, index, 1);
335 : }
336 :
337 : inline JSAtom *
338 687195 : js::StaticStrings::getLength2(jschar c1, jschar c2)
339 : {
340 687195 : JS_ASSERT(fitsInSmallChar(c1));
341 687195 : JS_ASSERT(fitsInSmallChar(c2));
342 687195 : size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2];
343 687195 : return length2StaticTable[index];
344 : }
345 :
346 : inline JSAtom *
347 : js::StaticStrings::getLength2(uint32_t i)
348 : {
349 : JS_ASSERT(i < 100);
350 : return getLength2('0' + i / 10, '0' + i % 10);
351 : }
352 :
353 : /* Get a static atomized string for chars if possible. */
354 : inline JSAtom *
355 36463296 : js::StaticStrings::lookup(const jschar *chars, size_t length)
356 : {
357 36463296 : switch (length) {
358 : case 1:
359 3528332 : if (chars[0] < UNIT_STATIC_LIMIT)
360 2940731 : return getUnit(chars[0]);
361 587601 : return NULL;
362 : case 2:
363 778299 : if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
364 687195 : return getLength2(chars[0], chars[1]);
365 91104 : return NULL;
366 : case 3:
367 : /*
368 : * Here we know that JSString::intStringTable covers only 256 (or at least
369 : * not 1000 or more) chars. We rely on order here to resolve the unit vs.
370 : * int string/length-2 string atom identity issue by giving priority to unit
371 : * strings for "0" through "9" and length-2 strings for "10" through "99".
372 : */
373 : JS_STATIC_ASSERT(INT_STATIC_LIMIT <= 999);
374 5765628 : if ('1' <= chars[0] && chars[0] <= '9' &&
375 112097 : '0' <= chars[1] && chars[1] <= '9' &&
376 111260 : '0' <= chars[2] && chars[2] <= '9') {
377 55603 : int i = (chars[0] - '0') * 100 +
378 55603 : (chars[1] - '0') * 10 +
379 111206 : (chars[2] - '0');
380 :
381 55603 : if (unsigned(i) < INT_STATIC_LIMIT)
382 1747 : return getInt(i);
383 : }
384 5540524 : return NULL;
385 : }
386 :
387 26614394 : return NULL;
388 : }
389 :
390 : JS_ALWAYS_INLINE void
391 26651446 : JSString::finalize(js::FreeOp *fop)
392 : {
393 : /* Shorts are in a different arena. */
394 26651446 : JS_ASSERT(!isShort());
395 :
396 26651446 : if (isFlat())
397 7531811 : asFlat().finalize(fop);
398 : else
399 19119635 : JS_ASSERT(isDependent() || isRope());
400 26651446 : }
401 :
402 : inline void
403 8337681 : JSFlatString::finalize(js::FreeOp *fop)
404 : {
405 8337681 : JS_ASSERT(!isShort());
406 :
407 : /*
408 : * This check depends on the fact that 'chars' is only initialized to the
409 : * beginning of inlineStorage. E.g., this is not the case for short strings.
410 : */
411 8337681 : if (chars() != d.inlineStorage)
412 7159453 : fop->free_(const_cast<jschar *>(chars()));
413 8337681 : }
414 :
415 : inline void
416 44335786 : JSShortString::finalize(js::FreeOp *fop)
417 : {
418 44335786 : JS_ASSERT(JSString::isShort());
419 44335786 : }
420 :
421 : inline void
422 2793319 : JSAtom::finalize(js::FreeOp *fop)
423 : {
424 2793319 : JS_ASSERT(JSString::isAtom());
425 2793319 : if (getAllocKind() == js::gc::FINALIZE_STRING)
426 805870 : JSFlatString::finalize(fop);
427 : else
428 1987449 : JS_ASSERT(getAllocKind() == js::gc::FINALIZE_SHORT_STRING);
429 2793319 : }
430 :
431 : inline void
432 2000 : JSExternalString::finalize(js::FreeOp *fop)
433 : {
434 2000 : const JSStringFinalizer *fin = externalFinalizer();
435 2000 : fin->finalize(fin, const_cast<jschar *>(chars()));
436 2000 : }
437 :
438 : namespace js {
439 :
440 : static JS_ALWAYS_INLINE JSFixedString *
441 92037040 : NewShortString(JSContext *cx, const jschar *chars, size_t length)
442 : {
443 : /*
444 : * Don't bother trying to find a static atom; measurement shows that not
445 : * many get here (for one, Atomize is catching them).
446 : */
447 92037040 : JS_ASSERT(JSShortString::lengthFits(length));
448 92037040 : JSInlineString *str = JSInlineString::lengthFits(length)
449 : ? JSInlineString::new_(cx)
450 92037040 : : JSShortString::new_(cx);
451 92037040 : if (!str)
452 0 : return NULL;
453 :
454 92037040 : jschar *storage = str->init(length);
455 92037040 : PodCopy(storage, chars, length);
456 92037040 : storage[length] = 0;
457 92037040 : Probes::createString(cx, str, length);
458 92037040 : return str;
459 : }
460 :
461 : } /* namespace js */
462 :
463 : #endif
|