/[jscoverage]/trunk/js/jsstr.cpp
ViewVC logotype

Contents of /trunk/js/jsstr.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 5 months ago) by siliconforks
File size: 198796 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
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 Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or 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 /*
42 * JS string type implementation.
43 *
44 * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these
45 * native methods store strings (possibly newborn) converted from their 'this'
46 * parameter and arguments on the stack: 'this' conversions at argv[-1], arg
47 * conversions at their index (argv[0], argv[1]). This is a legitimate method
48 * of rooting things that might lose their newborn root due to subsequent GC
49 * allocations in the same native method.
50 */
51 #define __STDC_LIMIT_MACROS
52
53 #include <stdlib.h>
54 #include <string.h>
55 #include "jstypes.h"
56 #include "jsstdint.h"
57 #include "jsutil.h" /* Added by JSIFY */
58 #include "jshash.h" /* Added by JSIFY */
59 #include "jsprf.h"
60 #include "jsapi.h"
61 #include "jsarray.h"
62 #include "jsatom.h"
63 #include "jsbool.h"
64 #include "jsbuiltins.h"
65 #include "jscntxt.h"
66 #include "jsversion.h"
67 #include "jsgc.h"
68 #include "jsinterp.h"
69 #include "jslock.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsopcode.h"
73 #include "jsregexp.h"
74 #include "jsscope.h"
75 #include "jsstaticcheck.h"
76 #include "jsstr.h"
77 #include "jsbit.h"
78 #include "jsvector.h"
79 #include "jsstrinlines.h"
80
81 #define JSSTRDEP_RECURSION_LIMIT 100
82
83 JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
84 JS_STATIC_ASSERT(INT_FITS_IN_JSVAL(JSString::MAX_LENGTH));
85
86 static size_t
87 MinimizeDependentStrings(JSString *str, int level, JSString **basep)
88 {
89 JSString *base;
90 size_t start, length;
91
92 JS_ASSERT(str->isDependent());
93 base = str->dependentBase();
94 start = str->dependentStart();
95 if (base->isDependent()) {
96 if (level < JSSTRDEP_RECURSION_LIMIT) {
97 start += MinimizeDependentStrings(base, level + 1, &base);
98 } else {
99 do {
100 start += base->dependentStart();
101 base = base->dependentBase();
102 } while (base->isDependent());
103 }
104 if (start == 0) {
105 JS_ASSERT(str->dependentIsPrefix());
106 str->prefixSetBase(base);
107 } else if (start <= JSString::MAX_DEPENDENT_START) {
108 length = str->dependentLength();
109 str->reinitDependent(base, start, length);
110 }
111 }
112 *basep = base;
113 return start;
114 }
115
116 jschar *
117 js_GetDependentStringChars(JSString *str)
118 {
119 size_t start;
120 JSString *base;
121
122 start = MinimizeDependentStrings(str, 0, &base);
123 JS_ASSERT(start < base->flatLength());
124 return base->flatChars() + start;
125 }
126
127 const jschar *
128 js_GetStringChars(JSContext *cx, JSString *str)
129 {
130 if (!js_MakeStringImmutable(cx, str))
131 return NULL;
132 return str->flatChars();
133 }
134
135 JSString * JS_FASTCALL
136 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
137 {
138 size_t rn, ln, lrdist, n;
139 jschar *ls, *s;
140 const jschar *rs;
141 JSString *ldep; /* non-null if left should become dependent */
142 JSString *str;
143
144 right->getCharsAndLength(rs, rn);
145 if (rn == 0)
146 return left;
147
148 left->getCharsAndLength(const_cast<const jschar *&>(ls), ln);
149 if (ln == 0)
150 return right;
151
152 if (!left->isMutable()) {
153 /* We must copy if left does not own a buffer to realloc. */
154 s = (jschar *) cx->malloc((ln + rn + 1) * sizeof(jschar));
155 if (!s)
156 return NULL;
157 js_strncpy(s, ls, ln);
158 ldep = NULL;
159 } else {
160 /* We can realloc left's space and make it depend on our result. */
161 JS_ASSERT(left->isFlat());
162 s = (jschar *) cx->realloc(ls, (ln + rn + 1) * sizeof(jschar));
163 if (!s)
164 return NULL;
165
166 /* Take care: right could depend on left! */
167 lrdist = (size_t)(rs - ls);
168 if (lrdist < ln)
169 rs = s + lrdist;
170 left->mChars = ls = s;
171 ldep = left;
172 }
173
174 js_strncpy(s + ln, rs, rn);
175 n = ln + rn;
176 s[n] = 0;
177
178 str = js_NewString(cx, s, n);
179 if (!str) {
180 /* Out of memory: clean up any space we (re-)allocated. */
181 if (!ldep) {
182 cx->free(s);
183 } else {
184 s = (jschar *) cx->realloc(ls, (ln + 1) * sizeof(jschar));
185 if (s)
186 left->mChars = s;
187 }
188 } else {
189 str->flatSetMutable();
190
191 /* Morph left into a dependent prefix if we realloc'd its buffer. */
192 if (ldep) {
193 ldep->reinitPrefix(str, ln);
194 #ifdef DEBUG
195 {
196 JSRuntime *rt = cx->runtime;
197 JS_RUNTIME_METER(rt, liveDependentStrings);
198 JS_RUNTIME_METER(rt, totalDependentStrings);
199 JS_LOCK_RUNTIME_VOID(rt,
200 (rt->strdepLengthSum += (double)ln,
201 rt->strdepLengthSquaredSum += (double)ln * (double)ln));
202 }
203 #endif
204 }
205 }
206
207 return str;
208 }
209
210 const jschar *
211 js_UndependString(JSContext *cx, JSString *str)
212 {
213 size_t n, size;
214 jschar *s;
215
216 if (str->isDependent()) {
217 n = str->dependentLength();
218 size = (n + 1) * sizeof(jschar);
219 s = (jschar *) cx->malloc(size);
220 if (!s)
221 return NULL;
222
223 js_strncpy(s, str->dependentChars(), n);
224 s[n] = 0;
225 str->reinitFlat(s, n);
226
227 #ifdef DEBUG
228 {
229 JSRuntime *rt = cx->runtime;
230 JS_RUNTIME_UNMETER(rt, liveDependentStrings);
231 JS_RUNTIME_UNMETER(rt, totalDependentStrings);
232 JS_LOCK_RUNTIME_VOID(rt,
233 (rt->strdepLengthSum -= (double)n,
234 rt->strdepLengthSquaredSum -= (double)n * (double)n));
235 }
236 #endif
237 }
238
239 return str->flatChars();
240 }
241
242 JSBool
243 js_MakeStringImmutable(JSContext *cx, JSString *str)
244 {
245 if (str->isDependent() && !js_UndependString(cx, str)) {
246 JS_RUNTIME_METER(cx->runtime, badUndependStrings);
247 return JS_FALSE;
248 }
249 str->flatClearMutable();
250 return JS_TRUE;
251 }
252
253 static JSString *
254 ArgToRootedString(JSContext *cx, uintN argc, jsval *vp, uintN arg)
255 {
256 if (arg >= argc)
257 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
258 vp += 2 + arg;
259
260 if (!JSVAL_IS_PRIMITIVE(*vp) && !JSVAL_TO_OBJECT(*vp)->defaultValue(cx, JSTYPE_STRING, vp))
261 return NULL;
262
263 JSString *str;
264 if (JSVAL_IS_STRING(*vp)) {
265 str = JSVAL_TO_STRING(*vp);
266 } else if (JSVAL_IS_BOOLEAN(*vp)) {
267 str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
268 JSVAL_TO_BOOLEAN(*vp)? 1 : 0]);
269 } else if (JSVAL_IS_NULL(*vp)) {
270 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
271 } else if (JSVAL_IS_VOID(*vp)) {
272 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
273 }
274 else {
275 if (JSVAL_IS_INT(*vp)) {
276 str = js_NumberToString(cx, JSVAL_TO_INT(*vp));
277 } else {
278 JS_ASSERT(JSVAL_IS_DOUBLE(*vp));
279 str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(*vp));
280 }
281 if (str)
282 *vp = STRING_TO_JSVAL(str);
283 }
284 return str;
285 }
286
287 /*
288 * Forward declarations for URI encode/decode and helper routines
289 */
290 static JSBool
291 str_decodeURI(JSContext *cx, uintN argc, jsval *vp);
292
293 static JSBool
294 str_decodeURI_Component(JSContext *cx, uintN argc, jsval *vp);
295
296 static JSBool
297 str_encodeURI(JSContext *cx, uintN argc, jsval *vp);
298
299 static JSBool
300 str_encodeURI_Component(JSContext *cx, uintN argc, jsval *vp);
301
302 static const uint32 OVERLONG_UTF8 = UINT32_MAX;
303
304 static uint32
305 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
306
307 /*
308 * Contributions from the String class to the set of methods defined for the
309 * global object. escape and unescape used to be defined in the Mocha library,
310 * but as ECMA decided to spec them, they've been moved to the core engine
311 * and made ECMA-compliant. (Incomplete escapes are interpreted as literal
312 * characters by unescape.)
313 */
314
315 /*
316 * Stuff to emulate the old libmocha escape, which took a second argument
317 * giving the type of escape to perform. Retained for compatibility, and
318 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
319 */
320
321 #define URL_XALPHAS ((uint8) 1)
322 #define URL_XPALPHAS ((uint8) 2)
323 #define URL_PATH ((uint8) 4)
324
325 static const uint8 urlCharType[256] =
326 /* Bit 0 xalpha -- the alphas
327 * Bit 1 xpalpha -- as xalpha but
328 * converts spaces to plus and plus to %20
329 * Bit 2 ... path -- as xalphas but doesn't escape '/'
330 */
331 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
332 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
333 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
334 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
335 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
336 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
337 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
338 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
339 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
340 0, };
341
342 /* This matches the ECMA escape set when mask is 7 (default.) */
343
344 #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
345
346 /* See ECMA-262 Edition 3 B.2.1 */
347 JSBool
348 js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
349 {
350 JSString *str;
351 size_t i, ni, length, newlength;
352 const jschar *chars;
353 jschar *newchars;
354 jschar ch;
355 jsint mask;
356 jsdouble d;
357 const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
358 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
359
360 mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
361 if (argc > 1) {
362 d = js_ValueToNumber(cx, &argv[1]);
363 if (JSVAL_IS_NULL(argv[1]))
364 return JS_FALSE;
365 if (!JSDOUBLE_IS_FINITE(d) ||
366 (mask = (jsint)d) != d ||
367 mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
368 {
369 char numBuf[12];
370 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
371 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
372 JSMSG_BAD_STRING_MASK, numBuf);
373 return JS_FALSE;
374 }
375 }
376
377 str = ArgToRootedString(cx, argc, argv - 2, 0);
378 if (!str)
379 return JS_FALSE;
380
381 str->getCharsAndLength(chars, length);
382 newlength = length;
383
384 /* Take a first pass and see how big the result string will need to be. */
385 for (i = 0; i < length; i++) {
386 if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
387 continue;
388 if (ch < 256) {
389 if (mask == URL_XPALPHAS && ch == ' ')
390 continue; /* The character will be encoded as '+' */
391 newlength += 2; /* The character will be encoded as %XX */
392 } else {
393 newlength += 5; /* The character will be encoded as %uXXXX */
394 }
395
396 /*
397 * This overflow test works because newlength is incremented by at
398 * most 5 on each iteration.
399 */
400 if (newlength < length) {
401 js_ReportAllocationOverflow(cx);
402 return JS_FALSE;
403 }
404 }
405
406 if (newlength >= ~(size_t)0 / sizeof(jschar)) {
407 js_ReportAllocationOverflow(cx);
408 return JS_FALSE;
409 }
410
411 newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
412 if (!newchars)
413 return JS_FALSE;
414 for (i = 0, ni = 0; i < length; i++) {
415 if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
416 newchars[ni++] = ch;
417 } else if (ch < 256) {
418 if (mask == URL_XPALPHAS && ch == ' ') {
419 newchars[ni++] = '+'; /* convert spaces to pluses */
420 } else {
421 newchars[ni++] = '%';
422 newchars[ni++] = digits[ch >> 4];
423 newchars[ni++] = digits[ch & 0xF];
424 }
425 } else {
426 newchars[ni++] = '%';
427 newchars[ni++] = 'u';
428 newchars[ni++] = digits[ch >> 12];
429 newchars[ni++] = digits[(ch & 0xF00) >> 8];
430 newchars[ni++] = digits[(ch & 0xF0) >> 4];
431 newchars[ni++] = digits[ch & 0xF];
432 }
433 }
434 JS_ASSERT(ni == newlength);
435 newchars[newlength] = 0;
436
437 str = js_NewString(cx, newchars, newlength);
438 if (!str) {
439 cx->free(newchars);
440 return JS_FALSE;
441 }
442 *rval = STRING_TO_JSVAL(str);
443 return JS_TRUE;
444 }
445 #undef IS_OK
446
447 static JSBool
448 str_escape(JSContext *cx, uintN argc, jsval *vp)
449 {
450 JSObject *obj;
451
452 obj = JS_THIS_OBJECT(cx, vp);
453 return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
454 }
455
456 /* See ECMA-262 Edition 3 B.2.2 */
457 static JSBool
458 str_unescape(JSContext *cx, uintN argc, jsval *vp)
459 {
460 JSString *str;
461 size_t i, ni, length;
462 const jschar *chars;
463 jschar *newchars;
464 jschar ch;
465
466 str = ArgToRootedString(cx, argc, vp, 0);
467 if (!str)
468 return JS_FALSE;
469
470 str->getCharsAndLength(chars, length);
471
472 /* Don't bother allocating less space for the new string. */
473 newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
474 if (!newchars)
475 return JS_FALSE;
476 ni = i = 0;
477 while (i < length) {
478 ch = chars[i++];
479 if (ch == '%') {
480 if (i + 1 < length &&
481 JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
482 {
483 ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
484 i += 2;
485 } else if (i + 4 < length && chars[i] == 'u' &&
486 JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
487 JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
488 {
489 ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
490 + JS7_UNHEX(chars[i + 2])) << 4)
491 + JS7_UNHEX(chars[i + 3])) << 4)
492 + JS7_UNHEX(chars[i + 4]);
493 i += 5;
494 }
495 }
496 newchars[ni++] = ch;
497 }
498 newchars[ni] = 0;
499
500 str = js_NewString(cx, newchars, ni);
501 if (!str) {
502 cx->free(newchars);
503 return JS_FALSE;
504 }
505 *vp = STRING_TO_JSVAL(str);
506 return JS_TRUE;
507 }
508
509 #if JS_HAS_UNEVAL
510 static JSBool
511 str_uneval(JSContext *cx, uintN argc, jsval *vp)
512 {
513 JSString *str;
514
515 str = js_ValueToSource(cx, argc != 0 ? vp[2] : JSVAL_VOID);
516 if (!str)
517 return JS_FALSE;
518 *vp = STRING_TO_JSVAL(str);
519 return JS_TRUE;
520 }
521 #endif
522
523 const char js_escape_str[] = "escape";
524 const char js_unescape_str[] = "unescape";
525 #if JS_HAS_UNEVAL
526 const char js_uneval_str[] = "uneval";
527 #endif
528 const char js_decodeURI_str[] = "decodeURI";
529 const char js_encodeURI_str[] = "encodeURI";
530 const char js_decodeURIComponent_str[] = "decodeURIComponent";
531 const char js_encodeURIComponent_str[] = "encodeURIComponent";
532
533 static JSFunctionSpec string_functions[] = {
534 JS_FN(js_escape_str, str_escape, 1,0),
535 JS_FN(js_unescape_str, str_unescape, 1,0),
536 #if JS_HAS_UNEVAL
537 JS_FN(js_uneval_str, str_uneval, 1,0),
538 #endif
539 JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
540 JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
541 JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
542 JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
543
544 JS_FS_END
545 };
546
547 jschar js_empty_ucstr[] = {0};
548 JSSubString js_EmptySubString = {0, js_empty_ucstr};
549
550 static JSBool
551 str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
552 {
553 jsval v;
554 JSString *str;
555
556 if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
557 if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) {
558 /* Follow ECMA-262 by fetching intrinsic length of our string. */
559 v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
560 JS_ASSERT(JSVAL_IS_STRING(v));
561 str = JSVAL_TO_STRING(v);
562 } else {
563 /* Preserve compatibility: convert obj to a string primitive. */
564 str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
565 if (!str)
566 return JS_FALSE;
567 }
568
569 *vp = INT_TO_JSVAL((jsint) str->length());
570 }
571
572 return JS_TRUE;
573 }
574
575 #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
576
577 static JSBool
578 str_enumerate(JSContext *cx, JSObject *obj)
579 {
580 jsval v;
581 JSString *str, *str1;
582 size_t i, length;
583
584 v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
585 JS_ASSERT(JSVAL_IS_STRING(v));
586 str = JSVAL_TO_STRING(v);
587
588 length = str->length();
589 for (i = 0; i < length; i++) {
590 str1 = js_NewDependentString(cx, str, i, 1);
591 if (!str1)
592 return JS_FALSE;
593 if (!obj->defineProperty(cx, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL,
594 STRING_ELEMENT_ATTRS)) {
595 return JS_FALSE;
596 }
597 }
598 return JS_TRUE;
599 }
600
601 static JSBool
602 str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
603 JSObject **objp)
604 {
605 jsval v;
606 JSString *str, *str1;
607 jsint slot;
608
609 if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING))
610 return JS_TRUE;
611
612 v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
613 JS_ASSERT(JSVAL_IS_STRING(v));
614 str = JSVAL_TO_STRING(v);
615
616 slot = JSVAL_TO_INT(id);
617 if ((size_t)slot < str->length()) {
618 str1 = JSString::getUnitString(cx, str, size_t(slot));
619 if (!str1)
620 return JS_FALSE;
621 if (!obj->defineProperty(cx, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL,
622 STRING_ELEMENT_ATTRS)) {
623 return JS_FALSE;
624 }
625 *objp = obj;
626 }
627 return JS_TRUE;
628 }
629
630 JSClass js_StringClass = {
631 js_String_str,
632 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
633 JSCLASS_HAS_CACHED_PROTO(JSProto_String),
634 JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub,
635 str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, NULL,
636 JSCLASS_NO_OPTIONAL_MEMBERS
637 };
638
639 #define NORMALIZE_THIS(cx,vp,str) \
640 JS_BEGIN_MACRO \
641 if (JSVAL_IS_STRING(vp[1])) { \
642 str = JSVAL_TO_STRING(vp[1]); \
643 } else { \
644 str = NormalizeThis(cx, vp); \
645 if (!str) \
646 return JS_FALSE; \
647 } \
648 JS_END_MACRO
649
650 static JSString *
651 NormalizeThis(JSContext *cx, jsval *vp)
652 {
653 JSString *str;
654
655 if (JSVAL_IS_NULL(vp[1]) && JSVAL_IS_NULL(JS_THIS(cx, vp)))
656 return NULL;
657 str = js_ValueToString(cx, vp[1]);
658 if (!str)
659 return NULL;
660 vp[1] = STRING_TO_JSVAL(str);
661 return str;
662 }
663
664 #if JS_HAS_TOSOURCE
665
666 /*
667 * String.prototype.quote is generic (as are most string methods), unlike
668 * toSource, toString, and valueOf.
669 */
670 static JSBool
671 str_quote(JSContext *cx, uintN argc, jsval *vp)
672 {
673 JSString *str;
674
675 NORMALIZE_THIS(cx, vp, str);
676 str = js_QuoteString(cx, str, '"');
677 if (!str)
678 return JS_FALSE;
679 *vp = STRING_TO_JSVAL(str);
680 return JS_TRUE;
681 }
682
683 static JSBool
684 str_toSource(JSContext *cx, uintN argc, jsval *vp)
685 {
686 jsval v;
687 JSString *str;
688 size_t i, j, k, n;
689 char buf[16];
690 const jschar *s;
691 jschar *t;
692
693 if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &v))
694 return JS_FALSE;
695 JS_ASSERT(JSVAL_IS_STRING(v));
696 str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"');
697 if (!str)
698 return JS_FALSE;
699 j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name);
700 str->getCharsAndLength(s, k);
701 n = j + k + 2;
702 t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
703 if (!t)
704 return JS_FALSE;
705 for (i = 0; i < j; i++)
706 t[i] = buf[i];
707 for (j = 0; j < k; i++, j++)
708 t[i] = s[j];
709 t[i++] = ')';
710 t[i++] = ')';
711 t[i] = 0;
712 str = js_NewString(cx, t, n);
713 if (!str) {
714 cx->free(t);
715 return JS_FALSE;
716 }
717 *vp = STRING_TO_JSVAL(str);
718 return JS_TRUE;
719 }
720
721 #endif /* JS_HAS_TOSOURCE */
722
723 JSBool
724 js_str_toString(JSContext *cx, uintN argc, jsval *vp)
725 {
726 return js_GetPrimitiveThis(cx, vp, &js_StringClass, vp);
727 }
728
729 /*
730 * Java-like string native methods.
731 */
732
733 static JSString *
734 SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end)
735 {
736 if (begin < 0)
737 begin = 0;
738 else if (begin > length)
739 begin = length;
740
741 if (end < 0)
742 end = 0;
743 else if (end > length)
744 end = length;
745 if (end < begin) {
746 /* ECMA emulates old JDK1.0 java.lang.String.substring. */
747 jsdouble tmp = begin;
748 begin = end;
749 end = tmp;
750 }
751
752 return js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin));
753 }
754
755 static JSBool
756 str_substring(JSContext *cx, uintN argc, jsval *vp)
757 {
758 JSString *str;
759 jsdouble d;
760 jsdouble length, begin, end;
761
762 NORMALIZE_THIS(cx, vp, str);
763 if (argc != 0) {
764 d = js_ValueToNumber(cx, &vp[2]);
765 if (JSVAL_IS_NULL(vp[2]))
766 return JS_FALSE;
767 length = str->length();
768 begin = js_DoubleToInteger(d);
769 if (argc == 1) {
770 end = length;
771 } else {
772 d = js_ValueToNumber(cx, &vp[3]);
773 if (JSVAL_IS_NULL(vp[3]))
774 return JS_FALSE;
775 end = js_DoubleToInteger(d);
776 }
777
778 str = SubstringTail(cx, str, length, begin, end);
779 if (!str)
780 return JS_FALSE;
781 }
782 *vp = STRING_TO_JSVAL(str);
783 return JS_TRUE;
784 }
785
786 #ifdef JS_TRACER
787 static JSString* FASTCALL
788 String_p_toString(JSContext* cx, JSObject* obj)
789 {
790 if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL))
791 return NULL;
792 jsval v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
793 JS_ASSERT(JSVAL_IS_STRING(v));
794 return JSVAL_TO_STRING(v);
795 }
796 #endif
797
798 JSString* JS_FASTCALL
799 js_toLowerCase(JSContext *cx, JSString *str)
800 {
801 size_t i, n;
802 const jschar *s;
803 jschar *news;
804
805 str->getCharsAndLength(s, n);
806 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
807 if (!news)
808 return NULL;
809 for (i = 0; i < n; i++)
810 news[i] = JS_TOLOWER(s[i]);
811 news[n] = 0;
812 str = js_NewString(cx, news, n);
813 if (!str) {
814 cx->free(news);
815 return NULL;
816 }
817 return str;
818 }
819
820 static JSBool
821 str_toLowerCase(JSContext *cx, uintN argc, jsval *vp)
822 {
823 JSString *str;
824
825 NORMALIZE_THIS(cx, vp, str);
826 str = js_toLowerCase(cx, str);
827 if (!str)
828 return JS_FALSE;
829 *vp = STRING_TO_JSVAL(str);
830 return JS_TRUE;
831 }
832
833 static JSBool
834 str_toLocaleLowerCase(JSContext *cx, uintN argc, jsval *vp)
835 {
836 JSString *str;
837
838 /*
839 * Forcefully ignore the first (or any) argument and return toLowerCase(),
840 * ECMA has reserved that argument, presumably for defining the locale.
841 */
842 if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
843 NORMALIZE_THIS(cx, vp, str);
844 return cx->localeCallbacks->localeToLowerCase(cx, str, vp);
845 }
846 return str_toLowerCase(cx, 0, vp);
847 }
848
849 JSString* JS_FASTCALL
850 js_toUpperCase(JSContext *cx, JSString *str)
851 {
852 size_t i, n;
853 const jschar *s;
854 jschar *news;
855
856 str->getCharsAndLength(s, n);
857 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
858 if (!news)
859 return NULL;
860 for (i = 0; i < n; i++)
861 news[i] = JS_TOUPPER(s[i]);
862 news[n] = 0;
863 str = js_NewString(cx, news, n);
864 if (!str) {
865 cx->free(news);
866 return NULL;
867 }
868 return str;
869 }
870
871 static JSBool
872 str_toUpperCase(JSContext *cx, uintN argc, jsval *vp)
873 {
874 JSString *str;
875
876 NORMALIZE_THIS(cx, vp, str);
877 str = js_toUpperCase(cx, str);
878 if (!str)
879 return JS_FALSE;
880 *vp = STRING_TO_JSVAL(str);
881 return JS_TRUE;
882 }
883
884 static JSBool
885 str_toLocaleUpperCase(JSContext *cx, uintN argc, jsval *vp)
886 {
887 JSString *str;
888
889 /*
890 * Forcefully ignore the first (or any) argument and return toUpperCase(),
891 * ECMA has reserved that argument, presumably for defining the locale.
892 */
893 if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
894 NORMALIZE_THIS(cx, vp, str);
895 return cx->localeCallbacks->localeToUpperCase(cx, str, vp);
896 }
897 return str_toUpperCase(cx, 0, vp);
898 }
899
900 static JSBool
901 str_localeCompare(JSContext *cx, uintN argc, jsval *vp)
902 {
903 JSString *str, *thatStr;
904
905 NORMALIZE_THIS(cx, vp, str);
906 if (argc == 0) {
907 *vp = JSVAL_ZERO;
908 } else {
909 thatStr = js_ValueToString(cx, vp[2]);
910 if (!thatStr)
911 return JS_FALSE;
912 if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
913 vp[2] = STRING_TO_JSVAL(thatStr);
914 return cx->localeCallbacks->localeCompare(cx, str, thatStr, vp);
915 }
916 *vp = INT_TO_JSVAL(js_CompareStrings(str, thatStr));
917 }
918 return JS_TRUE;
919 }
920
921 static JSBool
922 str_charAt(JSContext *cx, uintN argc, jsval *vp)
923 {
924 jsval t;
925 JSString *str;
926 jsint i;
927 jsdouble d;
928
929 t = vp[1];
930 if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {
931 str = JSVAL_TO_STRING(t);
932 i = JSVAL_TO_INT(vp[2]);
933 if ((size_t)i >= str->length())
934 goto out_of_range;
935 } else {
936 str = NormalizeThis(cx, vp);
937 if (!str)
938 return JS_FALSE;
939
940 if (argc == 0) {
941 d = 0.0;
942 } else {
943 d = js_ValueToNumber(cx, &vp[2]);
944 if (JSVAL_IS_NULL(vp[2]))
945 return JS_FALSE;
946 d = js_DoubleToInteger(d);
947 }
948
949 if (d < 0 || str->length() <= d)
950 goto out_of_range;
951 i = (jsint) d;
952 }
953
954 str = JSString::getUnitString(cx, str, size_t(i));
955 if (!str)
956 return JS_FALSE;
957 *vp = STRING_TO_JSVAL(str);
958 return JS_TRUE;
959
960 out_of_range:
961 *vp = JS_GetEmptyStringValue(cx);
962 return JS_TRUE;
963 }
964
965 static JSBool
966 str_charCodeAt(JSContext *cx, uintN argc, jsval *vp)
967 {
968 jsval t;
969 JSString *str;
970 jsint i;
971 jsdouble d;
972
973 t = vp[1];
974 if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {
975 str = JSVAL_TO_STRING(t);
976 i = JSVAL_TO_INT(vp[2]);
977 if ((size_t)i >= str->length())
978 goto out_of_range;
979 } else {
980 str = NormalizeThis(cx, vp);
981 if (!str)
982 return JS_FALSE;
983
984 if (argc == 0) {
985 d = 0.0;
986 } else {
987 d = js_ValueToNumber(cx, &vp[2]);
988 if (JSVAL_IS_NULL(vp[2]))
989 return JS_FALSE;
990 d = js_DoubleToInteger(d);
991 }
992
993 if (d < 0 || str->length() <= d)
994 goto out_of_range;
995 i = (jsint) d;
996 }
997
998 *vp = INT_TO_JSVAL(str->chars()[i]);
999 return JS_TRUE;
1000
1001 out_of_range:
1002 *vp = JS_GetNaNValue(cx);
1003 return JS_TRUE;
1004 }
1005
1006 #ifdef JS_TRACER
1007 extern jsdouble js_NaN;
1008
1009 jsdouble FASTCALL
1010 js_String_p_charCodeAt(JSString* str, jsdouble d)
1011 {
1012 d = js_DoubleToInteger(d);
1013 if (d < 0 || (int32)str->length() <= d)
1014 return js_NaN;
1015 return jsdouble(str->chars()[jsuint(d)]);
1016 }
1017
1018 int32 FASTCALL
1019 js_String_p_charCodeAt_int(JSString* str, jsint i)
1020 {
1021 if (i < 0 || (int32)str->length() <= i)
1022 return 0;
1023 return str->chars()[i];
1024 }
1025 JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int, STRING, INT32, 1, 1)
1026
1027 jsdouble FASTCALL
1028 js_String_p_charCodeAt0(JSString* str)
1029 {
1030 if ((int32)str->length() == 0)
1031 return js_NaN;
1032 return jsdouble(str->chars()[0]);
1033 }
1034
1035 /*
1036 * The FuncFilter replaces the generic double version of charCodeAt with the
1037 * integer fast path if appropriate.
1038 */
1039 int32 FASTCALL
1040 js_String_p_charCodeAt0_int(JSString* str)
1041 {
1042 if ((int32)str->length() == 0)
1043 return 0;
1044 return str->chars()[0];
1045 }
1046 JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING, 1, 1)
1047 #endif
1048
1049 jsint
1050 js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1051 const jschar *pat, jsuint patlen)
1052 {
1053 uint8 skip[sBMHCharSetSize];
1054
1055 JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1056 for (jsuint i = 0; i < sBMHCharSetSize; i++)
1057 skip[i] = (uint8)patlen;
1058 jsuint m = patlen - 1;
1059 for (jsuint i = 0; i < m; i++) {
1060 jschar c = pat[i];
1061 if (c >= sBMHCharSetSize)
1062 return sBMHBadPattern;
1063 skip[c] = (uint8)(m - i);
1064 }
1065 jschar c;
1066 for (jsuint k = m;
1067 k < textlen;
1068 k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1069 for (jsuint i = k, j = m; ; i--, j--) {
1070 if (text[i] != pat[j])
1071 break;
1072 if (j == 0)
1073 return static_cast<jsint>(i); /* safe: max string size */
1074 }
1075 }
1076 return -1;
1077 }
1078
1079 static JS_ALWAYS_INLINE jsint
1080 StringMatch(const jschar *text, jsuint textlen,
1081 const jschar *pat, jsuint patlen)
1082 {
1083 if (patlen == 0)
1084 return 0;
1085 if (textlen < patlen)
1086 return -1;
1087
1088 #if __i386__
1089 /*
1090 * Given enough registers, the unrolled loop below is faster than the
1091 * following loop. 32-bit x86 does not have enough registers.
1092 */
1093 if (patlen == 1) {
1094 const jschar p0 = *pat;
1095 for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
1096 if (*c == p0)
1097 return c - text;
1098 }
1099 return -1;
1100 }
1101 #endif
1102
1103 /*
1104 * XXX tune the BMH threshold (512) and pattern-length threshold (2).
1105 * If the text or pattern strings are short, BMH will be more expensive
1106 * than the basic linear scan.
1107 */
1108 if (textlen >= 512 && jsuint(patlen - 2) <= sBMHPatLenMax - 2) {
1109 jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
1110 if (index != sBMHBadPattern)
1111 return index;
1112 }
1113
1114 const jschar *textend = text + textlen - (patlen - 1);
1115 const jschar *patend = pat + patlen;
1116 const jschar p0 = *pat;
1117 const jschar *patNext = pat + 1;
1118 uint8 fixup;
1119
1120 #if __APPLE__ && __GNUC__ && __i386__
1121 /*
1122 * It is critical that |t| is kept in a register. The version of gcc we use
1123 * to build on 32-bit Mac does not realize this. See bug 526173.
1124 */
1125 register const jschar *t asm("esi") = text;
1126 #else
1127 const jschar *t = text;
1128 #endif
1129
1130 /* Credit: Duff */
1131 switch ((textend - text) & 7) {
1132 do {
1133 case 0: if (*t++ == p0) { fixup = 8; goto match; }
1134 case 7: if (*t++ == p0) { fixup = 7; goto match; }
1135 case 6: if (*t++ == p0) { fixup = 6; goto match; }
1136 case 5: if (*t++ == p0) { fixup = 5; goto match; }
1137 case 4: if (*t++ == p0) { fixup = 4; goto match; }
1138 case 3: if (*t++ == p0) { fixup = 3; goto match; }
1139 case 2: if (*t++ == p0) { fixup = 2; goto match; }
1140 case 1: if (*t++ == p0) { fixup = 1; goto match; }
1141 continue;
1142 do {
1143 if (*t++ == p0) {
1144 match:
1145 for (const jschar *p1 = patNext, *t1 = t;
1146 p1 != patend;
1147 ++p1, ++t1) {
1148 if (*p1 != *t1)
1149 goto failed_match;
1150 }
1151 return t - text - 1;
1152 }
1153 failed_match:;
1154 } while (--fixup > 0);
1155 } while(t != textend);
1156 }
1157 return -1;
1158 }
1159
1160 static JSBool
1161 str_indexOf(JSContext *cx, uintN argc, jsval *vp)
1162 {
1163
1164 JSString *str;
1165 NORMALIZE_THIS(cx, vp, str);
1166
1167 JSString *patstr = ArgToRootedString(cx, argc, vp, 0);
1168 if (!patstr)
1169 return JS_FALSE;
1170
1171 const jschar *text = str->chars();
1172 jsuint textlen = str->length();
1173 const jschar *pat = patstr->chars();
1174 jsuint patlen = patstr->length();
1175
1176 jsuint start;
1177 if (argc > 1) {
1178 jsval indexVal = vp[3];
1179 if (JSVAL_IS_INT(indexVal)) {
1180 jsint i = JSVAL_TO_INT(indexVal);
1181 if (i <= 0) {
1182 start = 0;
1183 } else if (jsuint(i) > textlen) {
1184 start = 0;
1185 textlen = 0;
1186 } else {
1187 start = i;
1188 text += start;
1189 textlen -= start;
1190 }
1191 } else {
1192 jsdouble d = js_ValueToNumber(cx, &vp[3]);
1193 if (JSVAL_IS_NULL(vp[3]))
1194 return JS_FALSE;
1195 d = js_DoubleToInteger(d);
1196 if (d <= 0) {
1197 start = 0;
1198 } else if (d > textlen) {
1199 start = 0;
1200 textlen = 0;
1201 } else {
1202 start = (jsint)d;
1203 text += start;
1204 textlen -= start;
1205 }
1206 }
1207 } else {
1208 start = 0;
1209 }
1210
1211 jsint match = StringMatch(text, textlen, pat, patlen);
1212 *vp = INT_TO_JSVAL((match == -1) ? -1 : start + match);
1213 return true;
1214 }
1215
1216 static JSBool
1217 str_lastIndexOf(JSContext *cx, uintN argc, jsval *vp)
1218 {
1219 JSString *str, *str2;
1220 const jschar *text, *pat;
1221 jsint i, j, textlen, patlen;
1222 jsdouble d;
1223
1224 NORMALIZE_THIS(cx, vp, str);
1225 text = str->chars();
1226 textlen = (jsint) str->length();
1227
1228 if (argc != 0 && JSVAL_IS_STRING(vp[2])) {
1229 str2 = JSVAL_TO_STRING(vp[2]);
1230 } else {
1231 str2 = ArgToRootedString(cx, argc, vp, 0);
1232 if (!str2)
1233 return JS_FALSE;
1234 }
1235 pat = str2->chars();
1236 patlen = (jsint) str2->length();
1237
1238 i = textlen - patlen; // Start searching here
1239 if (i < 0) {
1240 *vp = INT_TO_JSVAL(-1);
1241 return JS_TRUE;
1242 }
1243
1244 if (argc > 1) {
1245 if (JSVAL_IS_INT(vp[3])) {
1246 j = JSVAL_TO_INT(vp[3]);
1247 if (j <= 0)
1248 i = 0;
1249 else if (j < i)
1250 i = j;
1251 } else {
1252 d = js_ValueToNumber(cx, &vp[3]);
1253 if (JSVAL_IS_NULL(vp[3]))
1254 return JS_FALSE;
1255 if (!JSDOUBLE_IS_NaN(d)) {
1256 d = js_DoubleToInteger(d);
1257 if (d <= 0)
1258 i = 0;
1259 else if (d < i)
1260 i = (jsint)d;
1261 }
1262 }
1263 }
1264
1265 if (patlen == 0) {
1266 *vp = INT_TO_JSVAL(i);
1267 return JS_TRUE;
1268 }
1269
1270 const jschar *t = text + i;
1271 const jschar *textend = text - 1;
1272 const jschar p0 = *pat;
1273 const jschar *patNext = pat + 1;
1274 const jschar *patEnd = pat + patlen;
1275
1276 for (; t != textend; --t) {
1277 if (*t == p0) {
1278 const jschar *t1 = t + 1;
1279 for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
1280 if (*t1 != *p1)
1281 goto break_continue;
1282 }
1283 *vp = INT_TO_JSVAL(t - text);
1284 return JS_TRUE;
1285 }
1286 break_continue:;
1287 }
1288
1289 *vp = INT_TO_JSVAL(-1);
1290 return JS_TRUE;
1291 }
1292
1293 static JSBool
1294 js_TrimString(JSContext *cx, jsval *vp, JSBool trimLeft, JSBool trimRight)
1295 {
1296 JSString *str;
1297 const jschar *chars;
1298 size_t length, begin, end;
1299
1300 NORMALIZE_THIS(cx, vp, str);
1301 str->getCharsAndLength(chars, length);
1302 begin = 0;
1303 end = length;
1304
1305 if (trimLeft) {
1306 while (begin < length && JS_ISSPACE(chars[begin]))
1307 ++begin;
1308 }
1309
1310 if (trimRight) {
1311 while (end > begin && JS_ISSPACE(chars[end-1]))
1312 --end;
1313 }
1314
1315 str = js_NewDependentString(cx, str, begin, end - begin);
1316 if (!str)
1317 return JS_FALSE;
1318
1319 *vp = STRING_TO_JSVAL(str);
1320 return JS_TRUE;
1321 }
1322
1323 static JSBool
1324 str_trim(JSContext *cx, uintN argc, jsval *vp)
1325 {
1326 return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
1327 }
1328
1329 static JSBool
1330 str_trimLeft(JSContext *cx, uintN argc, jsval *vp)
1331 {
1332 return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
1333 }
1334
1335 static JSBool
1336 str_trimRight(JSContext *cx, uintN argc, jsval *vp)
1337 {
1338 return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
1339 }
1340
1341 /*
1342 * Perl-inspired string functions.
1343 */
1344
1345 /*
1346 * RegExpGuard factors logic out of String regexp operations. After each
1347 * operation completes, RegExpGuard data members become available, according to
1348 * the comments below.
1349 *
1350 * Notes on parameters to RegExpGuard member functions:
1351 * - 'optarg' indicates in which argument position RegExp flags will be found,
1352 * if present. This is a Mozilla extension and not part of any ECMA spec.
1353 * - 'flat' indicates that the given pattern string will not be interpreted as
1354 * a regular expression, hence regexp meta-characters are ignored.
1355 */
1356 class RegExpGuard
1357 {
1358 RegExpGuard(const RegExpGuard &);
1359 void operator=(const RegExpGuard &);
1360
1361 JSContext *mCx;
1362 JSObject *mReobj;
1363 JSRegExp *mRe;
1364
1365 public:
1366 RegExpGuard(JSContext *cx) : mCx(cx), mRe(NULL) {}
1367
1368 ~RegExpGuard() {
1369 if (mRe)
1370 DROP_REGEXP(mCx, mRe);
1371 }
1372
1373 /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
1374 bool
1375 init(uintN argc, jsval *vp)
1376 {
1377 jsval patval = vp[2];
1378 if (argc != 0 && VALUE_IS_REGEXP(mCx, patval)) {
1379 mReobj = JSVAL_TO_OBJECT(patval);
1380 mRe = (JSRegExp *) mReobj->getPrivate();
1381 HOLD_REGEXP(mCx, mRe);
1382 } else {
1383 patstr = ArgToRootedString(mCx, argc, vp, 0);
1384 if (!patstr)
1385 return false;
1386 }
1387 return true;
1388 }
1389
1390 /*
1391 * Upper bound on the number of characters we are willing to potentially
1392 * waste on searching for RegExp meta-characters.
1393 */
1394 static const size_t sMaxFlatPatLen = 256;
1395
1396 /*
1397 * Attempt to match |patstr| with |textstr|. Return false if flat matching
1398 * could not be used.
1399 */
1400 bool
1401 tryFlatMatch(JSString *textstr, bool flat, uintN optarg, uintN argc)
1402 {
1403 if (mRe)
1404 return false;
1405 patstr->getCharsAndLength(pat, patlen);
1406 if (optarg < argc ||
1407 (!flat &&
1408 (patlen > sMaxFlatPatLen || js_ContainsRegExpMetaChars(pat, patlen)))) {
1409 return false;
1410 }
1411 textstr->getCharsAndLength(text, textlen);
1412 match = StringMatch(text, textlen, pat, patlen);
1413 return true;
1414 }
1415
1416 /* Data available on successful return from |tryFlatMatch|. */
1417 JSString *patstr;
1418 const jschar *pat;
1419 size_t patlen;
1420 const jschar *text;
1421 size_t textlen;
1422 jsint match;
1423
1424 /* If the pattern is not already a regular expression, make it so. */
1425 bool
1426 normalizeRegExp(bool flat, uintN optarg, uintN argc, jsval *vp)
1427 {
1428 /* If we don't have a RegExp, build RegExp from pattern string. */
1429 if (mRe)
1430 return true;
1431 JSString *opt;
1432 if (optarg < argc) {
1433 opt = js_ValueToString(mCx, vp[2 + optarg]);
1434 if (!opt)
1435 return false;
1436 } else {
1437 opt = NULL;
1438 }
1439 mRe = js_NewRegExpOpt(mCx, patstr, opt, flat);
1440 if (!mRe)
1441 return false;
1442 mReobj = NULL;
1443 return true;
1444 }
1445
1446 /* Data available on successful return from |normalizeRegExp|. */
1447 JSObject *reobj() const { return mReobj; } /* nullable */
1448 JSRegExp *re() const { return mRe; } /* non-null */
1449 };
1450
1451 /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
1452 static JS_ALWAYS_INLINE bool
1453 Matched(bool test, jsval v)
1454 {
1455 return test ? (v == JSVAL_TRUE) : !JSVAL_IS_NULL(v);
1456 }
1457
1458 typedef bool (*DoMatchCallback)(JSContext *cx, size_t count, void *data);
1459
1460 /*
1461 * BitOR-ing these flags allows the DoMatch caller to control when how the
1462 * RegExp engine is called and when callbacks are fired.
1463 */
1464 enum MatchControlFlags {
1465 TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */
1466 TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */
1467 CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */
1468
1469 MATCH_ARGS = TEST_GLOBAL_BIT,
1470 MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
1471 REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
1472 };
1473
1474 /* Factor out looping and matching logic. */
1475 static bool
1476 DoMatch(JSContext *cx, jsval *vp, JSString *str, const RegExpGuard &g,
1477 DoMatchCallback callback, void *data, MatchControlFlags flags)
1478 {
1479 if (g.re()->flags & JSREG_GLOB) {
1480 /* global matching ('g') */
1481 bool testGlobal = flags & TEST_GLOBAL_BIT;
1482 if (g.reobj())
1483 js_ClearRegExpLastIndex(g.reobj());
1484 for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
1485 if (!js_ExecuteRegExp(cx, g.re(), str, &i, testGlobal, vp))
1486 return false;
1487 if (!Matched(testGlobal, *vp))
1488 break;
1489 if (!callback(cx, count, data))
1490 return false;
1491 if (cx->regExpStatics.lastMatch.length == 0)
1492 ++i;
1493 }
1494 } else {
1495 /* single match */
1496 bool testSingle = flags & TEST_SINGLE_BIT,
1497 callbackOnSingle = flags & CALLBACK_ON_SINGLE_BIT;
1498 size_t i = 0;
1499 if (!js_ExecuteRegExp(cx, g.re(), str, &i, testSingle, vp))
1500 return false;
1501 if (callbackOnSingle && Matched(testSingle, *vp) &&
1502 !callback(cx, 0, data)) {
1503 return false;
1504 }
1505 }
1506 return true;
1507 }
1508
1509 /*
1510 * DoMatch will only callback on global matches, hence this function builds
1511 * only the "array of matches" returned by match on global regexps.
1512 */
1513 static bool
1514 MatchCallback(JSContext *cx, size_t count, void *p)
1515 {
1516 JS_ASSERT(count <= JSVAL_INT_MAX); /* by max string length */
1517
1518 jsval &arrayval = *static_cast<jsval *>(p);
1519 JSObject *arrayobj = JSVAL_TO_OBJECT(arrayval);
1520 if (!arrayobj) {
1521 arrayobj = js_NewArrayObject(cx, 0, NULL);
1522 if (!arrayobj)
1523 return false;
1524 arrayval = OBJECT_TO_JSVAL(arrayobj);
1525 }
1526
1527 JSString *str = cx->regExpStatics.input;
1528 JSSubString &match = cx->regExpStatics.lastMatch;
1529 ptrdiff_t off = match.chars - str->chars();
1530 JS_ASSERT(off >= 0 && size_t(off) <= str->length());
1531 JSString *matchstr = js_NewDependentString(cx, str, off, match.length);
1532 if (!matchstr)
1533 return false;
1534
1535 jsval v = STRING_TO_JSVAL(matchstr);
1536
1537 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1538 return arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
1539 }
1540
1541 static bool
1542 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g,
1543 jsval *vp)
1544 {
1545 if (g.match < 0) {
1546 *vp = JSVAL_NULL;
1547 return true;
1548 }
1549
1550 /* For this non-global match, produce a RegExp.exec-style array. */
1551 JSObject *obj = js_NewSlowArrayObject(cx);
1552 if (!obj)
1553 return false;
1554 *vp = OBJECT_TO_JSVAL(obj);
1555
1556 return obj->defineProperty(cx, INT_TO_JSID(0), STRING_TO_JSVAL(g.patstr)) &&
1557 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
1558 INT_TO_JSVAL(g.match)) &&
1559 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
1560 STRING_TO_JSVAL(textstr));
1561 }
1562
1563 static JSBool
1564 str_match(JSContext *cx, uintN argc, jsval *vp)
1565 {
1566 JSString *str;
1567 NORMALIZE_THIS(cx, vp, str);
1568
1569 RegExpGuard g(cx);
1570 if (!g.init(argc, vp))
1571 return false;
1572 if (g.tryFlatMatch(str, false, 1, argc))
1573 return BuildFlatMatchArray(cx, str, g, vp);
1574 if (!g.normalizeRegExp(false, 1, argc, vp))
1575 return false;
1576
1577 JSAutoTempValueRooter array(cx, JSVAL_NULL);
1578 if (!DoMatch(cx, vp, str, g, MatchCallback, array.addr(), MATCH_ARGS))
1579 return false;
1580
1581 /* When not global, DoMatch will leave |RegEx.exec()| in *vp. */
1582 if (g.re()->flags & JSREG_GLOB)
1583 *vp = array.value();
1584 return true;
1585 }
1586
1587 static JSBool
1588 str_search(JSContext *cx, uintN argc, jsval *vp)
1589 {
1590 JSString *str;
1591 NORMALIZE_THIS(cx, vp, str);
1592
1593 RegExpGuard g(cx);
1594 if (!g.init(argc, vp))
1595 return false;
1596 if (g.tryFlatMatch(str, false, 1, argc)) {
1597 *vp = INT_TO_JSVAL(g.match);
1598 return true;
1599 }
1600 if (!g.normalizeRegExp(false, 1, argc, vp))
1601 return false;
1602
1603 size_t i = 0;
1604 if (!js_ExecuteRegExp(cx, g.re(), str, &i, true, vp))
1605 return false;
1606
1607 if (*vp == JSVAL_TRUE)
1608 *vp = INT_TO_JSVAL(cx->regExpStatics.leftContext.length);
1609 else
1610 *vp = INT_TO_JSVAL(-1);
1611 return true;
1612 }
1613
1614 struct ReplaceData {
1615 ReplaceData(JSContext *cx) : g(cx), cb(cx) {}
1616 JSString *str; /* 'this' parameter object as a string */
1617 RegExpGuard g; /* regexp parameter object and private data */
1618 JSObject *lambda; /* replacement function object or null */
1619 JSString *repstr; /* replacement string */
1620 jschar *dollar; /* null or pointer to first $ in repstr */
1621 jschar *dollarEnd; /* limit pointer for js_strchr_limit */
1622 jsint index; /* index in result of next replacement */
1623 jsint leftIndex; /* left context index in str->chars */
1624 JSSubString dollarStr; /* for "$$" InterpretDollar result */
1625 bool calledBack; /* record whether callback has been called */
1626 JSCharBuffer cb; /* buffer built during DoMatch */
1627 };
1628
1629 static JSSubString *
1630 InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
1631 size_t *skip)
1632 {
1633 JSRegExpStatics *res;
1634 jschar dc, *cp;
1635 uintN num, tmp;
1636
1637 JS_ASSERT(*dp == '$');
1638
1639 /* If there is only a dollar, bail now */
1640 if (dp + 1 >= ep)
1641 return NULL;
1642
1643 /* Interpret all Perl match-induced dollar variables. */
1644 res = &cx->regExpStatics;
1645 dc = dp[1];
1646 if (JS7_ISDEC(dc)) {
1647 /* ECMA-262 Edition 3: 1-9 or 01-99 */
1648 num = JS7_UNDEC(dc);
1649 if (num > res->parenCount)
1650 return NULL;
1651
1652 cp = dp + 2;
1653 if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
1654 tmp = 10 * num + JS7_UNDEC(dc);
1655 if (tmp <= res->parenCount) {
1656 cp++;
1657 num = tmp;
1658 }
1659 }
1660 if (num == 0)
1661 return NULL;
1662
1663 /* Adjust num from 1 $n-origin to 0 array-index-origin. */
1664 num--;
1665 *skip = cp - dp;
1666 return REGEXP_PAREN_SUBSTRING(res, num);
1667 }
1668
1669 *skip = 2;
1670 switch (dc) {
1671 case '$':
1672 rdata.dollarStr.chars = dp;
1673 rdata.dollarStr.length = 1;
1674 return &rdata.dollarStr;
1675 case '&':
1676 return &res->lastMatch;
1677 case '+':
1678 return &res->lastParen;
1679 case '`':
1680 return &res->leftContext;
1681 case '\'':
1682 return &res->rightContext;
1683 }
1684 return NULL;
1685 }
1686
1687 static JS_ALWAYS_INLINE bool
1688 PushRegExpSubstr(JSContext *cx, const JSSubString &sub, jsval *&sp)
1689 {
1690 JSString *whole = cx->regExpStatics.input;
1691 size_t off = sub.chars - whole->chars();
1692 JSString *str = js_NewDependentString(cx, whole, off, sub.length);
1693 if (!str)
1694 return false;
1695 *sp++ = STRING_TO_JSVAL(str);
1696 return true;
1697 }
1698
1699 static bool
1700 FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
1701 {
1702 JSString *repstr;
1703 size_t replen, skip;
1704 jschar *dp, *ep;
1705 JSSubString *sub;
1706 JSObject *lambda;
1707
1708 lambda = rdata.lambda;
1709 if (lambda) {
1710 uintN i, m, n;
1711
1712 js_LeaveTrace(cx);
1713
1714 /*
1715 * In the lambda case, not only do we find the replacement string's
1716 * length, we compute repstr and return it via rdata for use within
1717 * DoReplace. The lambda is called with arguments ($&, $1, $2, ...,
1718 * index, input), i.e., all the properties of a regexp match array.
1719 * For $&, etc., we must create string jsvals from cx->regExpStatics.
1720 * We grab up stack space to keep the newborn strings GC-rooted.
1721 */
1722 uintN p = rdata.g.re()->parenCount;
1723 uintN argc = 1 + p + 2;
1724 void *mark;
1725 jsval *invokevp = js_AllocStack(cx, 2 + argc, &mark);
1726 if (!invokevp)
1727 return false;
1728
1729 MUST_FLOW_THROUGH("lambda_out");
1730 bool ok = false;
1731 bool freeMoreParens = false;
1732
1733 /*
1734 * Save the regExpStatics from the current regexp, since they may be
1735 * clobbered by a RegExp usage in the lambda function. Note that all
1736 * members of JSRegExpStatics are JSSubStrings, so not GC roots, save
1737 * input, which is rooted otherwise via vp[1] in str_replace.
1738 */
1739 JSRegExpStatics save = cx->regExpStatics;
1740
1741 /* Push lambda and its 'this' parameter. */
1742 jsval *sp = invokevp;
1743 *sp++ = OBJECT_TO_JSVAL(lambda);
1744 *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));
1745
1746 /* Push $&, $1, $2, ... */
1747 if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
1748 goto lambda_out;
1749
1750 i = 0;
1751 m = cx->regExpStatics.parenCount;
1752 n = JS_MIN(m, 9);
1753 for (uintN j = 0; i < n; i++, j++) {
1754 if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
1755 goto lambda_out;
1756 }
1757 for (uintN j = 0; i < m; i++, j++) {
1758 if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp))
1759 goto lambda_out;
1760 }
1761
1762 /*
1763 * We need to clear moreParens in the top-of-stack cx->regExpStatics
1764 * so it won't be possibly realloc'ed, leaving the bottom-of-stack
1765 * moreParens pointing to freed memory.
1766 */
1767 cx->regExpStatics.moreParens = NULL;
1768 freeMoreParens = true;
1769
1770 /* Make sure to push undefined for any unmatched parens. */
1771 for (; i < p; i++)
1772 *sp++ = JSVAL_VOID;
1773
1774 /* Push match index and input string. */
1775 *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
1776 *sp++ = STRING_TO_JSVAL(rdata.str);
1777
1778 if (!js_Invoke(cx, argc, invokevp, 0))
1779 goto lambda_out;
1780
1781 /*
1782 * NB: we count on the newborn string root to hold any string
1783 * created by this js_ValueToString that would otherwise be GC-
1784 * able, until we use rdata.repstr in DoReplace.
1785 */
1786 repstr = js_ValueToString(cx, *invokevp);
1787 if (!repstr)
1788 goto lambda_out;
1789
1790 rdata.repstr = repstr;
1791 *sizep = repstr->length();
1792
1793 ok = true;
1794
1795 lambda_out:
1796 js_FreeStack(cx, mark);
1797 if (freeMoreParens)
1798 cx->free(cx->regExpStatics.moreParens);
1799 cx->regExpStatics = save;
1800 return ok;
1801 }
1802
1803 repstr = rdata.repstr;
1804 replen = repstr->length();
1805 for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
1806 dp = js_strchr_limit(dp, '$', ep)) {
1807 sub = InterpretDollar(cx, dp, ep, rdata, &skip);
1808 if (sub) {
1809 replen += sub->length - skip;
1810 dp += skip;
1811 }
1812 else
1813 dp++;
1814 }
1815 *sizep = replen;
1816 return true;
1817 }
1818
1819 static void
1820 DoReplace(JSContext *cx, ReplaceData &rdata, jschar *chars)
1821 {
1822 JSString *repstr;
1823 jschar *bp, *cp, *dp, *ep;
1824 size_t len, skip;
1825 JSSubString *sub;
1826
1827 repstr = rdata.repstr;
1828 bp = cp = repstr->chars();
1829 for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
1830 dp = js_strchr_limit(dp, '$', ep)) {
1831 len = dp - cp;
1832 js_strncpy(chars, cp, len);
1833 chars += len;
1834 cp = dp;
1835 sub = InterpretDollar(cx, dp, ep, rdata, &skip);
1836 if (sub) {
1837 len = sub->length;
1838 js_strncpy(chars, sub->chars, len);
1839 chars += len;
1840 cp += skip;
1841 dp += skip;
1842 } else {
1843 dp++;
1844 }
1845 }
1846 js_strncpy(chars, cp, repstr->length() - (cp - bp));
1847 }
1848
1849 static bool
1850 ReplaceCallback(JSContext *cx, size_t count, void *p)
1851 {
1852 ReplaceData &rdata = *static_cast<ReplaceData *>(p);
1853
1854 rdata.calledBack = true;
1855 JSString *str = rdata.str;
1856 size_t leftoff = rdata.leftIndex;
1857 const jschar *left = str->chars() + leftoff;
1858 size_t leftlen = cx->regExpStatics.lastMatch.chars - left;
1859 rdata.leftIndex = cx->regExpStatics.lastMatch.chars - str->chars();
1860 rdata.leftIndex += cx->regExpStatics.lastMatch.length;
1861
1862 size_t replen = 0; /* silence 'unused' warning */
1863 if (!FindReplaceLength(cx, rdata, &replen))
1864 return false;
1865
1866 size_t growth = leftlen + replen;
1867 if (!rdata.cb.growBy(growth))
1868 return false;
1869
1870 jschar *chars = rdata.cb.begin() + rdata.index;
1871 rdata.index += growth;
1872 js_strncpy(chars, left, leftlen);
1873 chars += leftlen;
1874 DoReplace(cx, rdata, chars);
1875 return true;
1876 }
1877
1878 static bool
1879 BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
1880 const RegExpGuard &g, jsval *vp)
1881 {
1882 if (g.match == -1) {
1883 *vp = STRING_TO_JSVAL(textstr);
1884 return true;
1885 }
1886
1887 const jschar *rep;
1888 size_t replen;
1889 repstr->getCharsAndLength(rep, replen);
1890
1891 JSCharBuffer cb(cx);
1892 if (!cb.reserve(g.textlen - g.patlen + replen) ||
1893 !cb.append(g.text, static_cast<size_t>(g.match)) ||
1894 !cb.append(rep, replen) ||
1895 !cb.append(g.text + g.match + g.patlen, g.text + g.textlen)) {
1896 return false;
1897 }
1898
1899 JSString *str = js_NewStringFromCharBuffer(cx, cb);
1900 if (!str)
1901 return false;
1902 *vp = STRING_TO_JSVAL(str);
1903 return true;
1904 }
1905
1906 static JSBool
1907 str_replace(JSContext *cx, uintN argc, jsval *vp)
1908 {
1909 ReplaceData rdata(cx);
1910 NORMALIZE_THIS(cx, vp, rdata.str);
1911
1912 /* Extract replacement string/function. */
1913 if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {
1914 rdata.lambda = JSVAL_TO_OBJECT(vp[3]);
1915 rdata.repstr = NULL;
1916 rdata.dollar = rdata.dollarEnd = NULL;
1917 } else {
1918 rdata.lambda = NULL;
1919 rdata.repstr = ArgToRootedString(cx, argc, vp, 1);
1920 if (!rdata.repstr)
1921 return false;
1922
1923 /* We're about to store pointers into the middle of our string. */
1924 if (!js_MakeStringImmutable(cx, rdata.repstr))
1925 return false;
1926 rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length();
1927 rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$',
1928 rdata.dollarEnd);
1929 }
1930
1931 if (!rdata.g.init(argc, vp))
1932 return false;
1933 if (!rdata.dollar && !rdata.lambda &&
1934 rdata.g.tryFlatMatch(rdata.str, true, 2, argc)) {
1935 return BuildFlatReplacement(cx, rdata.str, rdata.repstr, rdata.g, vp);
1936 }
1937 if (!rdata.g.normalizeRegExp(true, 2, argc, vp))
1938 return false;
1939
1940 rdata.index = 0;
1941 rdata.leftIndex = 0;
1942 rdata.calledBack = false;
1943
1944 if (!DoMatch(cx, vp, rdata.str, rdata.g, ReplaceCallback, &rdata, REPLACE_ARGS))
1945 return false;
1946
1947 if (!rdata.calledBack) {
1948 /* Didn't match, so the string is unmodified. */
1949 *vp = STRING_TO_JSVAL(rdata.str);
1950 return true;
1951 }
1952
1953 JSSubString *sub = &cx->regExpStatics.rightContext;
1954 if (!rdata.cb.append(sub->chars, sub->length))
1955 return false;
1956
1957 JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
1958 if (!retstr)
1959 return false;
1960
1961 *vp = STRING_TO_JSVAL(retstr);
1962 return true;
1963 }
1964
1965 /*
1966 * Subroutine used by str_split to find the next split point in str, starting
1967 * at offset *ip and looking either for the separator substring given by sep, or
1968 * for the next re match. In the re case, return the matched separator in *sep,
1969 * and the possibly updated offset in *ip.
1970 *
1971 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
1972 * separator occurrence if found, or str->length if no separator is found.
1973 */
1974 static jsint
1975 find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip,
1976 JSSubString *sep)
1977 {
1978 jsint i, j, k;
1979 size_t length;
1980 jschar *chars;
1981
1982 /*
1983 * Stop if past end of string. If at end of string, we will compare the
1984 * null char stored there (by js_NewString*) to sep->chars[j] in the while
1985 * loop at the end of this function, so that
1986 *
1987 * "ab,".split(',') => ["ab", ""]
1988 *
1989 * and the resulting array converts back to the string "ab," for symmetry.
1990 * However, we ape Perl and do this only if there is a sufficiently large
1991 * limit argument (see str_split).
1992 */
1993 i = *ip;
1994 length = str->length();
1995 if ((size_t)i > length)
1996 return -1;
1997
1998 chars = str->chars();
1999
2000 /*
2001 * Match a regular expression against the separator at or above index i.
2002 * Call js_ExecuteRegExp with true for the test argument. On successful
2003 * match, get the separator from cx->regExpStatics.lastMatch.
2004 */
2005 if (re) {
2006 size_t index;
2007 jsval rval;
2008
2009 again:
2010 /* JS1.2 deviated from Perl by never matching at end of string. */
2011 index = (size_t)i;
2012 if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval))
2013 return -2;
2014 if (rval != JSVAL_TRUE) {
2015 /* Mismatch: ensure our caller advances i past end of string. */
2016 sep->length = 1;
2017 return length;
2018 }
2019 i = (jsint)index;
2020 *sep = cx->regExpStatics.lastMatch;
2021 if (sep->length == 0) {
2022 /*
2023 * Empty string match: never split on an empty match at the start
2024 * of a find_split cycle. Same rule as for an empty global match
2025 * in DoMatch.
2026 */
2027 if (i == *ip) {
2028 /*
2029 * "Bump-along" to avoid sticking at an empty match, but don't
2030 * bump past end of string -- our caller must do that by adding
2031 * sep->length to our return value.
2032 */
2033 if ((size_t)i == length)
2034 return -1;
2035 i++;
2036 goto again;
2037 }
2038 if ((size_t)i == length) {
2039 /*
2040 * If there was a trivial zero-length match at the end of the
2041 * split, then we shouldn't output the matched string at the end
2042 * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
2043 */
2044 sep->chars = NULL;
2045 }
2046 }
2047 JS_ASSERT((size_t)i >= sep->length);
2048 return i - sep->length;
2049 }
2050
2051 /*
2052 * Special case: if sep is the empty string, split str into one character
2053 * substrings. Let our caller worry about whether to split once at end of
2054 * string into an empty substring.
2055 */
2056 if (sep->length == 0)
2057 return ((size_t)i == length) ? -1 : i + 1;
2058
2059 /*
2060 * Now that we know sep is non-empty, search starting at i in str for an
2061 * occurrence of all of sep's chars. If we find them, return the index of
2062 * the first separator char. Otherwise, return length.
2063 */
2064 j = 0;
2065 while ((size_t)(k = i + j) < length) {
2066 if (chars[k] == sep->chars[j]) {
2067 if ((size_t)++j == sep->length)
2068 return i;
2069 } else {
2070 i++;
2071 j = 0;
2072 }
2073 }
2074 return k;
2075 }
2076
2077 static JSBool
2078 str_split(JSContext *cx, uintN argc, jsval *vp)
2079 {
2080 JSString *str, *sub;
2081 JSObject *arrayobj;
2082 jsval v;
2083 JSBool ok, limited;
2084 JSRegExp *re;
2085 JSSubString *sep, tmp;
2086 jsdouble d;
2087 jsint i, j;
2088 uint32 len, limit;
2089
2090 NORMALIZE_THIS(cx, vp, str);
2091
2092 arrayobj = js_NewArrayObject(cx, 0, NULL);
2093 if (!arrayobj)
2094 return JS_FALSE;
2095 *vp = OBJECT_TO_JSVAL(arrayobj);
2096
2097 if (argc == 0) {
2098 v = STRING_TO_JSVAL(str);
2099 ok = arrayobj->setProperty(cx, INT_TO_JSID(0), &v);
2100 } else {
2101 if (VALUE_IS_REGEXP(cx, vp[2])) {
2102 re = (JSRegExp *) JSVAL_TO_OBJECT(vp[2])->getPrivate();
2103 sep = &tmp;
2104
2105 /* Set a magic value so we can detect a successful re match. */
2106 sep->chars = NULL;
2107 sep->length = 0;
2108 } else {
2109 JSString *str2 = js_ValueToString(cx, vp[2]);
2110 if (!str2)
2111 return JS_FALSE;
2112 vp[2] = STRING_TO_JSVAL(str2);
2113
2114 /*
2115 * Point sep at a local copy of str2's header because find_split
2116 * will modify sep->length.
2117 */
2118 str2->getCharsAndLength(tmp.chars, tmp.length);
2119 sep = &tmp;
2120 re = NULL;
2121 }
2122
2123 /* Use the second argument as the split limit, if given. */
2124 limited = (argc > 1) && !JSVAL_IS_VOID(vp[3]);
2125 limit = 0; /* Avoid warning. */
2126 if (limited) {
2127 d = js_ValueToNumber(cx, &vp[3]);
2128 if (JSVAL_IS_NULL(vp[3]))
2129 return JS_FALSE;
2130
2131 /* Clamp limit between 0 and 1 + string length. */
2132 limit = js_DoubleToECMAUint32(d);
2133 if (limit > str->length())
2134 limit = 1 + str->length();
2135 }
2136
2137 len = i = 0;
2138 while ((j = find_split(cx, str, re, &i, sep)) >= 0) {
2139 if (limited && len >= limit)
2140 break;
2141 sub = js_NewDependentString(cx, str, i, (size_t)(j - i));
2142 if (!sub)
2143 return JS_FALSE;
2144 v = STRING_TO_JSVAL(sub);
2145 if (!JS_SetElement(cx, arrayobj, len, &v))
2146 return JS_FALSE;
2147 len++;
2148
2149 /*
2150 * Imitate perl's feature of including parenthesized substrings
2151 * that matched part of the delimiter in the new array, after the
2152 * split substring that was delimited.
2153 */
2154 if (re && sep->chars) {
2155 uintN num;
2156 JSSubString *parsub;
2157
2158 for (num = 0; num < cx->regExpStatics.parenCount; num++) {
2159 if (limited && len >= limit)
2160 break;
2161 parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
2162 sub = js_NewStringCopyN(cx, parsub->chars, parsub->length);
2163 if (!sub)
2164 return JS_FALSE;
2165 v = STRING_TO_JSVAL(sub);
2166 if (!JS_SetElement(cx, arrayobj, len, &v))
2167 return JS_FALSE;
2168 len++;
2169 }
2170 sep->chars = NULL;
2171 }
2172 i = j + sep->length;
2173 }
2174 ok = (j != -2);
2175 }
2176 return ok;
2177 }
2178
2179 #if JS_HAS_PERL_SUBSTR
2180 static JSBool
2181 str_substr(JSContext *cx, uintN argc, jsval *vp)
2182 {
2183 JSString *str;
2184 jsdouble d;
2185 jsdouble length, begin, end;
2186
2187 NORMALIZE_THIS(cx, vp, str);
2188 if (argc != 0) {
2189 d = js_ValueToNumber(cx, &vp[2]);
2190 if (JSVAL_IS_NULL(vp[2]))
2191 return JS_FALSE;
2192 length = str->length();
2193 begin = js_DoubleToInteger(d);
2194 if (begin < 0) {
2195 begin += length;
2196 if (begin < 0)
2197 begin = 0;
2198 } else if (begin > length) {
2199 begin = length;
2200 }
2201
2202 if (argc == 1) {
2203 end = length;
2204 } else {
2205 d = js_ValueToNumber(cx, &vp[3]);
2206 if (JSVAL_IS_NULL(vp[3]))
2207 return JS_FALSE;
2208 end = js_DoubleToInteger(d);
2209 if (end < 0)
2210 end = 0;
2211 end += begin;
2212 if (end > length)
2213 end = length;
2214 }
2215
2216 str = js_NewDependentString(cx, str,
2217 (size_t)begin,
2218 (size_t)(end - begin));
2219 if (!str)
2220 return JS_FALSE;
2221 }
2222 *vp = STRING_TO_JSVAL(str);
2223 return JS_TRUE;
2224 }
2225 #endif /* JS_HAS_PERL_SUBSTR */
2226
2227 /*
2228 * Python-esque sequence operations.
2229 */
2230 static JSBool
2231 str_concat(JSContext *cx, uintN argc, jsval *vp)
2232 {
2233 JSString *str, *str2;
2234 jsval *argv;
2235 uintN i;
2236
2237 NORMALIZE_THIS(cx, vp, str);
2238
2239 /* Set vp (aka rval) early to handle the argc == 0 case. */
2240 *vp = STRING_TO_JSVAL(str);
2241
2242 for (i = 0, argv = vp + 2; i < argc; i++) {
2243 str2 = js_ValueToString(cx, argv[i]);
2244 if (!str2)
2245 return JS_FALSE;
2246 argv[i] = STRING_TO_JSVAL(str2);
2247
2248 str = js_ConcatStrings(cx, str, str2);
2249 if (!str)
2250 return JS_FALSE;
2251 *vp = STRING_TO_JSVAL(str);
2252 }
2253
2254 return JS_TRUE;
2255 }
2256
2257 static JSBool
2258 str_slice(JSContext *cx, uintN argc, jsval *vp)
2259 {
2260 jsval t, v;
2261 JSString *str;
2262
2263 t = vp[1];
2264 v = vp[2];
2265 if (argc == 1 && JSVAL_IS_STRING(t) && JSVAL_IS_INT(v)) {
2266 size_t begin, end, length;
2267
2268 str = JSVAL_TO_STRING(t);
2269 begin = JSVAL_TO_INT(v);
2270 end = str->length();
2271 if (begin <= end) {
2272 length = end - begin;
2273 if (length == 0) {
2274 str = cx->runtime->emptyString;
2275 } else {
2276 str = (length == 1)
2277 ? JSString::getUnitString(cx, str, begin)
2278 : js_NewDependentString(cx, str, begin, length);
2279 if (!str)
2280 return JS_FALSE;
2281 }
2282 *vp = STRING_TO_JSVAL(str);
2283 return JS_TRUE;
2284 }
2285 }
2286
2287 NORMALIZE_THIS(cx, vp, str);
2288
2289 if (argc != 0) {
2290 double begin, end, length;
2291
2292 begin = js_ValueToNumber(cx, &vp[2]);
2293 if (JSVAL_IS_NULL(vp[2]))
2294 return JS_FALSE;
2295 begin = js_DoubleToInteger(begin);
2296 length = str->length();
2297 if (begin < 0) {
2298 begin += length;
2299 if (begin < 0)
2300 begin = 0;
2301 } else if (begin > length) {
2302 begin = length;
2303 }
2304
2305 if (argc == 1) {
2306 end = length;
2307 } else {
2308 end = js_ValueToNumber(cx, &vp[3]);
2309 if (JSVAL_IS_NULL(vp[3]))
2310 return JS_FALSE;
2311 end = js_DoubleToInteger(end);
2312 if (end < 0) {
2313 end += length;
2314 if (end < 0)
2315 end = 0;
2316 } else if (end > length) {
2317 end = length;
2318 }
2319 if (end < begin)
2320 end = begin;
2321 }
2322
2323 str = js_NewDependentString(cx, str,
2324 (size_t)begin,
2325 (size_t)(end - begin));
2326 if (!str)
2327 return JS_FALSE;
2328 }
2329 *vp = STRING_TO_JSVAL(str);
2330 return JS_TRUE;
2331 }
2332
2333 #if JS_HAS_STR_HTML_HELPERS
2334 /*
2335 * HTML composition aids.
2336 */
2337 static JSBool
2338 tagify(JSContext *cx, const char *begin, JSString *param, const char *end,
2339 jsval *vp)
2340 {
2341 JSString *str;
2342 jschar *tagbuf;
2343 size_t beglen, endlen, parlen, taglen;
2344 size_t i, j;
2345
2346 NORMALIZE_THIS(cx, vp, str);
2347
2348 if (!end)
2349 end = begin;
2350
2351 beglen = strlen(begin);
2352 taglen = 1 + beglen + 1; /* '<begin' + '>' */
2353 parlen = 0; /* Avoid warning. */
2354 if (param) {
2355 parlen = param->length();
2356 taglen += 2 + parlen + 1; /* '="param"' */
2357 }
2358 endlen = strlen(end);
2359 taglen += str->length() + 2 + endlen + 1; /* 'str</end>' */
2360
2361 if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2362 js_ReportAllocationOverflow(cx);
2363 return JS_FALSE;
2364 }
2365
2366 tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2367 if (!tagbuf)
2368 return JS_FALSE;
2369
2370 j = 0;
2371 tagbuf[j++] = '<';
2372 for (i = 0; i < beglen; i++)
2373 tagbuf[j++] = (jschar)begin[i];
2374 if (param) {
2375 tagbuf[j++] = '=';
2376 tagbuf[j++] = '"';
2377 js_strncpy(&tagbuf[j], param->chars(), parlen);
2378 j += parlen;
2379 tagbuf[j++] = '"';
2380 }
2381 tagbuf[j++] = '>';
2382 js_strncpy(&tagbuf[j], str->chars(), str->length());
2383 j += str->length();
2384 tagbuf[j++] = '<';
2385 tagbuf[j++] = '/';
2386 for (i = 0; i < endlen; i++)
2387 tagbuf[j++] = (jschar)end[i];
2388 tagbuf[j++] = '>';
2389 JS_ASSERT(j == taglen);
2390 tagbuf[j] = 0;
2391
2392 str = js_NewString(cx, tagbuf, taglen);
2393 if (!str) {
2394 js_free((char *)tagbuf);
2395 return JS_FALSE;
2396 }
2397 *vp = STRING_TO_JSVAL(str);
2398 return JS_TRUE;
2399 }
2400
2401 static JSBool
2402 tagify_value(JSContext *cx, uintN argc, jsval *vp,
2403 const char *begin, const char *end)
2404 {
2405 JSString *param;
2406
2407 param = ArgToRootedString(cx, argc, vp, 0);
2408 if (!param)
2409 return JS_FALSE;
2410 return tagify(cx, begin, param, end, vp);
2411 }
2412
2413 static JSBool
2414 str_bold(JSContext *cx, uintN argc, jsval *vp)
2415 {
2416 return tagify(cx, "b", NULL, NULL, vp);
2417 }
2418
2419 static JSBool
2420 str_italics(JSContext *cx, uintN argc, jsval *vp)
2421 {
2422 return tagify(cx, "i", NULL, NULL, vp);
2423 }
2424
2425 static JSBool
2426 str_fixed(JSContext *cx, uintN argc, jsval *vp)
2427 {
2428 return tagify(cx, "tt", NULL, NULL, vp);
2429 }
2430
2431 static JSBool
2432 str_fontsize(JSContext *cx, uintN argc, jsval *vp)
2433 {
2434 return tagify_value(cx, argc, vp, "font size", "font");
2435 }
2436
2437 static JSBool
2438 str_fontcolor(JSContext *cx, uintN argc, jsval *vp)
2439 {
2440 return tagify_value(cx, argc, vp, "font color", "font");
2441 }
2442
2443 static JSBool
2444 str_link(JSContext *cx, uintN argc, jsval *vp)
2445 {
2446 return tagify_value(cx, argc, vp, "a href", "a");
2447 }
2448
2449 static JSBool
2450 str_anchor(JSContext *cx, uintN argc, jsval *vp)
2451 {
2452 return tagify_value(cx, argc, vp, "a name", "a");
2453 }
2454
2455 static JSBool
2456 str_strike(JSContext *cx, uintN argc, jsval *vp)
2457 {
2458 return tagify(cx, "strike", NULL, NULL, vp);
2459 }
2460
2461 static JSBool
2462 str_small(JSContext *cx, uintN argc, jsval *vp)
2463 {
2464 return tagify(cx, "small", NULL, NULL, vp);
2465 }
2466
2467 static JSBool
2468 str_big(JSContext *cx, uintN argc, jsval *vp)
2469 {
2470 return tagify(cx, "big", NULL, NULL, vp);
2471 }
2472
2473 static JSBool
2474 str_blink(JSContext *cx, uintN argc, jsval *vp)
2475 {
2476 return tagify(cx, "blink", NULL, NULL, vp);
2477 }
2478
2479 static JSBool
2480 str_sup(JSContext *cx, uintN argc, jsval *vp)
2481 {
2482 return tagify(cx, "sup", NULL, NULL, vp);
2483 }
2484
2485 static JSBool
2486 str_sub(JSContext *cx, uintN argc, jsval *vp)
2487 {
2488 return tagify(cx, "sub", NULL, NULL, vp);
2489 }
2490 #endif /* JS_HAS_STR_HTML_HELPERS */
2491
2492 #ifdef JS_TRACER
2493 JSString* FASTCALL
2494 js_String_getelem(JSContext* cx, JSString* str, int32 i)
2495 {
2496 if ((size_t)i >= str->length())
2497 return NULL;
2498 return JSString::getUnitString(cx, str, size_t(i));
2499 }
2500 #endif
2501
2502 JS_DEFINE_TRCINFO_1(js_str_toString,
2503 (2, (extern, STRING_RETRY, String_p_toString, CONTEXT, THIS, 1, 1)))
2504 JS_DEFINE_TRCINFO_1(str_charAt,
2505 (3, (extern, STRING_RETRY, js_String_getelem, CONTEXT, THIS_STRING, INT32, 1, 1)))
2506 JS_DEFINE_TRCINFO_2(str_charCodeAt,
2507 (1, (extern, DOUBLE, js_String_p_charCodeAt0, THIS_STRING, 1, 1)),
2508 (2, (extern, DOUBLE, js_String_p_charCodeAt, THIS_STRING, DOUBLE, 1, 1)))
2509 JS_DEFINE_TRCINFO_1(str_concat,
2510 (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, 1, 1)))
2511
2512 #define GENERIC JSFUN_GENERIC_NATIVE
2513 #define PRIMITIVE JSFUN_THISP_PRIMITIVE
2514 #define GENERIC_PRIMITIVE (GENERIC | PRIMITIVE)
2515
2516 static JSFunctionSpec string_methods[] = {
2517 #if JS_HAS_TOSOURCE
2518 JS_FN("quote", str_quote, 0,GENERIC_PRIMITIVE),
2519 JS_FN(js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING),
2520 #endif
2521
2522 /* Java-like methods. */
2523 JS_TN(js_toString_str, js_str_toString, 0,JSFUN_THISP_STRING, &js_str_toString_trcinfo),
2524 JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_THISP_STRING),
2525 JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_THISP_STRING),
2526 JS_FN("substring", str_substring, 2,GENERIC_PRIMITIVE),
2527 JS_FN("toLowerCase", str_toLowerCase, 0,GENERIC_PRIMITIVE),
2528 JS_FN("toUpperCase", str_toUpperCase, 0,GENERIC_PRIMITIVE),
2529 JS_TN("charAt", str_charAt, 1,GENERIC_PRIMITIVE, &str_charAt_trcinfo),
2530 JS_TN("charCodeAt", str_charCodeAt, 1,GENERIC_PRIMITIVE, &str_charCodeAt_trcinfo),
2531 JS_FN("indexOf", str_indexOf, 1,GENERIC_PRIMITIVE),
2532 JS_FN("lastIndexOf", str_lastIndexOf, 1,GENERIC_PRIMITIVE),
2533 JS_FN("trim", str_trim, 0,GENERIC_PRIMITIVE),
2534 JS_FN("trimLeft", str_trimLeft, 0,GENERIC_PRIMITIVE),
2535 JS_FN("trimRight", str_trimRight, 0,GENERIC_PRIMITIVE),
2536 JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,GENERIC_PRIMITIVE),
2537 JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,GENERIC_PRIMITIVE),
2538 JS_FN("localeCompare", str_localeCompare, 1,GENERIC_PRIMITIVE),
2539
2540 /* Perl-ish methods (search is actually Python-esque). */
2541 JS_FN("match", str_match, 1,GENERIC_PRIMITIVE),
2542 JS_FN("search", str_search, 1,GENERIC_PRIMITIVE),
2543 JS_FN("replace", str_replace, 2,GENERIC_PRIMITIVE),
2544 JS_FN("split", str_split, 2,GENERIC_PRIMITIVE),
2545 #if JS_HAS_PERL_SUBSTR
2546 JS_FN("substr", str_substr, 2,GENERIC_PRIMITIVE),
2547 #endif
2548
2549 /* Python-esque sequence methods. */
2550 JS_TN("concat", str_concat, 1,GENERIC_PRIMITIVE, &str_concat_trcinfo),
2551 JS_FN("slice", str_slice, 2,GENERIC_PRIMITIVE),
2552
2553 /* HTML string methods. */
2554 #if JS_HAS_STR_HTML_HELPERS
2555 JS_FN("bold", str_bold, 0,PRIMITIVE),
2556 JS_FN("italics", str_italics, 0,PRIMITIVE),
2557 JS_FN("fixed", str_fixed, 0,PRIMITIVE),
2558 JS_FN("fontsize", str_fontsize, 1,PRIMITIVE),
2559 JS_FN("fontcolor", str_fontcolor, 1,PRIMITIVE),
2560 JS_FN("link", str_link, 1,PRIMITIVE),
2561 JS_FN("anchor", str_anchor, 1,PRIMITIVE),
2562 JS_FN("strike", str_strike, 0,PRIMITIVE),
2563 JS_FN("small", str_small, 0,PRIMITIVE),
2564 JS_FN("big", str_big, 0,PRIMITIVE),
2565 JS_FN("blink", str_blink, 0,PRIMITIVE),
2566 JS_FN("sup", str_sup, 0,PRIMITIVE),
2567 JS_FN("sub", str_sub, 0,PRIMITIVE),
2568 #endif
2569
2570 JS_FS_END
2571 };
2572
2573 #define C(c) c, 0x00
2574
2575 /*
2576 * String data for all unit strings (including zero-char backstop required for independent strings),
2577 * packed into single array.
2578 */
2579 static const jschar UnitStringData[] = {
2580 C(0x00), C(0x01), C(0x02), C(0x03), C(0x04), C(0x05), C(0x06), C(0x07),
2581 C(0x08), C(0x09), C(0x0a), C(0x0b), C(0x0c), C(0x0d), C(0x0e), C(0x0f),
2582 C(0x10), C(0x11), C(0x12), C(0x13), C(0x14), C(0x15), C(0x16), C(0x17),
2583 C(0x18), C(0x19), C(0x1a), C(0x1b), C(0x1c), C(0x1d), C(0x1e), C(0x1f),
2584 C(0x20), C(0x21), C(0x22), C(0x23), C(0x24), C(0x25), C(0x26), C(0x27),
2585 C(0x28), C(0x29), C(0x2a), C(0x2b), C(0x2c), C(0x2d), C(0x2e), C(0x2f),
2586 C(0x30), C(0x31), C(0x32), C(0x33), C(0x34), C(0x35), C(0x36), C(0x37),
2587 C(0x38), C(0x39), C(0x3a), C(0x3b), C(0x3c), C(0x3d), C(0x3e), C(0x3f),
2588 C(0x40), C(0x41), C(0x42), C(0x43), C(0x44), C(0x45), C(0x46), C(0x47),
2589 C(0x48), C(0x49), C(0x4a), C(0x4b), C(0x4c), C(0x4d), C(0x4e), C(0x4f),
2590 C(0x50), C(0x51), C(0x52), C(0x53), C(0x54), C(0x55), C(0x56), C(0x57),
2591 C(0x58), C(0x59), C(0x5a), C(0x5b), C(0x5c), C(0x5d), C(0x5e), C(0x5f),
2592 C(0x60), C(0x61), C(0x62), C(0x63), C(0x64), C(0x65), C(0x66), C(0x67),
2593 C(0x68), C(0x69), C(0x6a), C(0x6b), C(0x6c), C(0x6d), C(0x6e), C(0x6f),
2594 C(0x70), C(0x71), C(0x72), C(0x73), C(0x74), C(0x75), C(0x76), C(0x77),
2595 C(0x78), C(0x79), C(0x7a), C(0x7b), C(0x7c), C(0x7d), C(0x7e), C(0x7f),
2596 C(0x80), C(0x81), C(0x82), C(0x83), C(0x84), C(0x85), C(0x86), C(0x87),
2597 C(0x88), C(0x89), C(0x8a), C(0x8b), C(0x8c), C(0x8d), C(0x8e), C(0x8f),
2598 C(0x90), C(0x91), C(0x92), C(0x93), C(0x94), C(0x95), C(0x96), C(0x97),
2599 C(0x98), C(0x99), C(0x9a), C(0x9b), C(0x9c), C(0x9d), C(0x9e), C(0x9f),
2600 C(0xa0), C(0xa1), C(0xa2), C(0xa3), C(0xa4), C(0xa5), C(0xa6), C(0xa7),
2601 C(0xa8), C(0xa9), C(0xaa), C(0xab), C(0xac), C(0xad), C(0xae), C(0xaf),
2602 C(0xb0), C(0xb1), C(0xb2), C(0xb3), C(0xb4), C(0xb5), C(0xb6), C(0xb7),
2603 C(0xb8), C(0xb9), C(0xba), C(0xbb), C(0xbc), C(0xbd), C(0xbe), C(0xbf),
2604 C(0xc0), C(0xc1), C(0xc2), C(0xc3), C(0xc4), C(0xc5), C(0xc6), C(0xc7),
2605 C(0xc8), C(0xc9), C(0xca), C(0xcb), C(0xcc), C(0xcd), C(0xce), C(0xcf),
2606 C(0xd0), C(0xd1), C(0xd2), C(0xd3), C(0xd4), C(0xd5), C(0xd6), C(0xd7),
2607 C(0xd8), C(0xd9), C(0xda), C(0xdb), C(0xdc), C(0xdd), C(0xde), C(0xdf),
2608 C(0xe0), C(0xe1), C(0xe2), C(0xe3), C(0xe4), C(0xe5), C(0xe6), C(0xe7),
2609 C(0xe8), C(0xe9), C(0xea), C(0xeb), C(0xec), C(0xed), C(0xee), C(0xef),
2610 C(0xf0), C(0xf1), C(0xf2), C(0xf3), C(0xf4), C(0xf5), C(0xf6), C(0xf7),
2611 C(0xf8), C(0xf9), C(0xfa), C(0xfb), C(0xfc), C(0xfd), C(0xfe), C(0xff)
2612 };
2613
2614 #define U(c) { 1 | JSString::ATOMIZED, {(jschar *)UnitStringData + (c) * 2} }
2615
2616 #ifdef __SUNPRO_CC
2617 #pragma pack(8)
2618 #else
2619 #pragma pack(push, 8)
2620 #endif
2621
2622 JSString JSString::unitStringTable[]
2623 #ifdef __GNUC__
2624 __attribute__ ((aligned (8)))
2625 #endif
2626 = {
2627 U(0x00), U(0x01), U(0x02), U(0x03), U(0x04), U(0x05), U(0x06), U(0x07),
2628 U(0x08), U(0x09), U(0x0a), U(0x0b), U(0x0c), U(0x0d), U(0x0e), U(0x0f),
2629 U(0x10), U(0x11), U(0x12), U(0x13), U(0x14), U(0x15), U(0x16), U(0x17),
2630 U(0x18), U(0x19), U(0x1a), U(0x1b), U(0x1c), U(0x1d), U(0x1e), U(0x1f),
2631 U(0x20), U(0x21), U(0x22), U(0x23), U(0x24), U(0x25), U(0x26), U(0x27),
2632 U(0x28), U(0x29), U(0x2a), U(0x2b), U(0x2c), U(0x2d), U(0x2e), U(0x2f),
2633 U(0x30), U(0x31), U(0x32), U(0x33), U(0x34), U(0x35), U(0x36), U(0x37),
2634 U(0x38), U(0x39), U(0x3a), U(0x3b), U(0x3c), U(0x3d), U(0x3e), U(0x3f),
2635 U(0x40), U(0x41), U(0x42), U(0x43), U(0x44), U(0x45), U(0x46), U(0x47),
2636 U(0x48), U(0x49), U(0x4a), U(0x4b), U(0x4c), U(0x4d), U(0x4e), U(0x4f),
2637 U(0x50), U(0x51), U(0x52), U(0x53), U(0x54), U(0x55), U(0x56), U(0x57),
2638 U(0x58), U(0x59), U(0x5a), U(0x5b), U(0x5c), U(0x5d), U(0x5e), U(0x5f),
2639 U(0x60), U(0x61), U(0x62), U(0x63), U(0x64), U(0x65), U(0x66), U(0x67),
2640 U(0x68), U(0x69), U(0x6a), U(0x6b), U(0x6c), U(0x6d), U(0x6e), U(0x6f),
2641 U(0x70), U(0x71), U(0x72), U(0x73), U(0x74), U(0x75), U(0x76), U(0x77),
2642 U(0x78), U(0x79), U(0x7a), U(0x7b), U(0x7c), U(0x7d), U(0x7e), U(0x7f),
2643 U(0x80), U(0x81), U(0x82), U(0x83), U(0x84), U(0x85), U(0x86), U(0x87),
2644 U(0x88), U(0x89), U(0x8a), U(0x8b), U(0x8c), U(0x8d), U(0x8e), U(0x8f),
2645 U(0x90), U(0x91), U(0x92), U(0x93), U(0x94), U(0x95), U(0x96), U(0x97),
2646 U(0x98), U(0x99), U(0x9a), U(0x9b), U(0x9c), U(0x9d), U(0x9e), U(0x9f),
2647 U(0xa0), U(0xa1), U(0xa2), U(0xa3), U(0xa4), U(0xa5), U(0xa6), U(0xa7),
2648 U(0xa8), U(0xa9), U(0xaa), U(0xab), U(0xac), U(0xad), U(0xae), U(0xaf),
2649 U(0xb0), U(0xb1), U(0xb2), U(0xb3), U(0xb4), U(0xb5), U(0xb6), U(0xb7),
2650 U(0xb8), U(0xb9), U(0xba), U(0xbb), U(0xbc), U(0xbd), U(0xbe), U(0xbf),
2651 U(0xc0), U(0xc1), U(0xc2), U(0xc3), U(0xc4), U(0xc5), U(0xc6), U(0xc7),
2652 U(0xc8), U(0xc9), U(0xca), U(0xcb), U(0xcc), U(0xcd), U(0xce), U(0xcf),
2653 U(0xd0), U(0xd1), U(0xd2), U(0xd3), U(0xd4), U(0xd5), U(0xd6), U(0xd7),
2654 U(0xd8), U(0xd9), U(0xda), U(0xdb), U(0xdc), U(0xdd), U(0xde), U(0xdf),
2655 U(0xe0), U(0xe1), U(0xe2), U(0xe3), U(0xe4), U(0xe5), U(0xe6), U(0xe7),
2656 U(0xe8), U(0xe9), U(0xea), U(0xeb), U(0xec), U(0xed), U(0xee), U(0xef),
2657 U(0xf0), U(0xf1), U(0xf2), U(0xf3), U(0xf4), U(0xf5), U(0xf6), U(0xf7),
2658 U(0xf8), U(0xf9), U(0xfa), U(0xfb), U(0xfc), U(0xfd), U(0xfe), U(0xff)
2659 };
2660
2661 #ifdef __SUNPRO_CC
2662 #pragma pack(0)
2663 #else
2664 #pragma pack(pop)
2665 #endif
2666
2667 #undef U
2668
2669 #define O0(c) 0x30, C(c)
2670 #define O1(c) 0x31, C(c) /* template for 10 .. 19 */
2671 #define O2(c) 0x32, C(c) /* template for 20 .. 29 */
2672 #define O3(c) 0x33, C(c)
2673 #define O4(c) 0x34, C(c)
2674 #define O5(c) 0x35, C(c)
2675 #define O6(c) 0x36, C(c)
2676 #define O7(c) 0x37, C(c)
2677 #define O8(c) 0x38, C(c)
2678 #define O9(c) 0x39, C(c)
2679
2680 #define O10(c) 0x31, O0(c) /* template for 100 .. 109 */
2681 #define O11(c) 0x31, O1(c) /* template for 110 .. 119 */
2682 #define O12(c) 0x31, O2(c)
2683 #define O13(c) 0x31, O3(c)
2684 #define O14(c) 0x31, O4(c)
2685 #define O15(c) 0x31, O5(c)
2686 #define O16(c) 0x31, O6(c)
2687 #define O17(c) 0x31, O7(c)
2688 #define O18(c) 0x31, O8(c)
2689 #define O19(c) 0x31, O9(c)
2690 #define O20(c) 0x32, O0(c)
2691 #define O21(c) 0x32, O1(c)
2692 #define O22(c) 0x32, O2(c)
2693 #define O23(c) 0x32, O3(c)
2694 #define O24(c) 0x32, O4(c)
2695 #define O25(c) 0x32, O5(c)
2696
2697 /*
2698 * Array starts with 100, 101, 102... (0x31 0x30 0x30 0x00 for 100\0)
2699 * 100, 101, 102 also share the pointers to 0, 1, 2 ...
2700 * 110, 111, 112 also share the pointers to 10, 11, 12...
2701 */
2702 static const jschar Hundreds[] = {
2703 O10(0x30), O10(0x31), O10(0x32), O10(0x33), O10(0x34), O10(0x35), O10(0x36), O10(0x37), O10(0x38), O10(0x39),
2704 O11(0x30), O11(0x31), O11(0x32), O11(0x33), O11(0x34), O11(0x35), O11(0x36), O11(0x37), O11(0x38), O11(0x39),
2705 O12(0x30), O12(0x31), O12(0x32), O12(0x33), O12(0x34), O12(0x35), O12(0x36), O12(0x37), O12(0x38), O12(0x39),
2706 O13(0x30), O13(0x31), O13(0x32), O13(0x33), O13(0x34), O13(0x35), O13(0x36), O13(0x37), O13(0x38), O13(0x39),
2707 O14(0x30), O14(0x31), O14(0x32), O14(0x33), O14(0x34), O14(0x35), O14(0x36), O14(0x37), O14(0x38), O14(0x39),
2708 O15(0x30), O15(0x31), O15(0x32), O15(0x33), O15(0x34), O15(0x35), O15(0x36), O15(0x37), O15(0x38), O15(0x39),
2709 O16(0x30), O16(0x31), O16(0x32), O16(0x33), O16(0x34), O16(0x35), O16(0x36), O16(0x37), O16(0x38), O16(0x39),
2710 O17(0x30), O17(0x31), O17(0x32), O17(0x33), O17(0x34), O17(0x35), O17(0x36), O17(0x37), O17(0x38), O17(0x39),
2711 O18(0x30), O18(0x31), O18(0x32), O18(0x33), O18(0x34), O18(0x35), O18(0x36), O18(0x37), O18(0x38), O18(0x39),
2712 O19(0x30), O19(0x31), O19(0x32), O19(0x33), O19(0x34), O19(0x35), O19(0x36), O19(0x37), O19(0x38), O19(0x39),
2713 O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39),
2714 O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39),
2715 O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39),
2716 O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23(0x37), O23(0x38), O23(0x39),
2717 O24(0x30), O24(0x31), O24(0x32), O24(0x33), O24(0x34), O24(0x35), O24(0x36), O24(0x37), O24(0x38), O24(0x39),
2718 O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35)
2719 };
2720
2721 #define L1(c) { 1 | JSString::ATOMIZED, {(jschar *)Hundreds + 2 + (c) * 4} } /* length 1: 0..9 */
2722 #define L2(c) { 2 | JSString::ATOMIZED, {(jschar *)Hundreds + 41 + (c - 10) * 4} } /* length 2: 10..99 */
2723 #define L3(c) { 3 | JSString::ATOMIZED, {(jschar *)Hundreds + (c - 100) * 4} } /* length 3: 100..255 */
2724
2725 #ifdef __SUNPRO_CC
2726 #pragma pack(8)
2727 #else
2728 #pragma pack(push, 8)
2729 #endif
2730
2731 JSString JSString::intStringTable[]
2732 #ifdef __GNUC__
2733 __attribute__ ((aligned (8)))
2734 #endif
2735 = {
2736 L1(0x00), L1(0x01), L1(0x02), L1(0x03), L1(0x04), L1(0x05), L1(0x06), L1(0x07),
2737 L1(0x08), L1(0x09), L2(0x0a), L2(0x0b), L2(0x0c), L2(0x0d), L2(0x0e), L2(0x0f),
2738 L2(0x10), L2(0x11), L2(0x12), L2(0x13), L2(0x14), L2(0x15), L2(0x16), L2(0x17),
2739 L2(0x18), L2(0x19), L2(0x1a), L2(0x1b), L2(0x1c), L2(0x1d), L2(0x1e), L2(0x1f),
2740 L2(0x20), L2(0x21), L2(0x22), L2(0x23), L2(0x24), L2(0x25), L2(0x26), L2(0x27),
2741 L2(0x28), L2(0x29), L2(0x2a), L2(0x2b), L2(0x2c), L2(0x2d), L2(0x2e), L2(0x2f),
2742 L2(0x30), L2(0x31), L2(0x32), L2(0x33), L2(0x34), L2(0x35), L2(0x36), L2(0x37),
2743 L2(0x38), L2(0x39), L2(0x3a), L2(0x3b), L2(0x3c), L2(0x3d), L2(0x3e), L2(0x3f),
2744 L2(0x40), L2(0x41), L2(0x42), L2(0x43), L2(0x44), L2(0x45), L2(0x46), L2(0x47),
2745 L2(0x48), L2(0x49), L2(0x4a), L2(0x4b), L2(0x4c), L2(0x4d), L2(0x4e), L2(0x4f),
2746 L2(0x50), L2(0x51), L2(0x52), L2(0x53), L2(0x54), L2(0x55), L2(0x56), L2(0x57),
2747 L2(0x58), L2(0x59), L2(0x5a), L2(0x5b), L2(0x5c), L2(0x5d), L2(0x5e), L2(0x5f),
2748 L2(0x60), L2(0x61), L2(0x62), L2(0x63), L3(0x64), L3(0x65), L3(0x66), L3(0x67),
2749 L3(0x68), L3(0x69), L3(0x6a), L3(0x6b), L3(0x6c), L3(0x6d), L3(0x6e), L3(0x6f),
2750 L3(0x70), L3(0x71), L3(0x72), L3(0x73), L3(0x74), L3(0x75), L3(0x76), L3(0x77),
2751 L3(0x78), L3(0x79), L3(0x7a), L3(0x7b), L3(0x7c), L3(0x7d), L3(0x7e), L3(0x7f),
2752 L3(0x80), L3(0x81), L3(0x82), L3(0x83), L3(0x84), L3(0x85), L3(0x86), L3(0x87),
2753 L3(0x88), L3(0x89), L3(0x8a), L3(0x8b), L3(0x8c), L3(0x8d), L3(0x8e), L3(0x8f),
2754 L3(0x90), L3(0x91), L3(0x92), L3(0x93), L3(0x94), L3(0x95), L3(0x96), L3(0x97),
2755 L3(0x98), L3(0x99), L3(0x9a), L3(0x9b), L3(0x9c), L3(0x9d), L3(0x9e), L3(0x9f),
2756 L3(0xa0), L3(0xa1), L3(0xa2), L3(0xa3), L3(0xa4), L3(0xa5), L3(0xa6), L3(0xa7),
2757 L3(0xa8), L3(0xa9), L3(0xaa), L3(0xab), L3(0xac), L3(0xad), L3(0xae), L3(0xaf),
2758 L3(0xb0), L3(0xb1), L3(0xb2), L3(0xb3), L3(0xb4), L3(0xb5), L3(0xb6), L3(0xb7),
2759 L3(0xb8), L3(0xb9), L3(0xba), L3(0xbb), L3(0xbc), L3(0xbd), L3(0xbe), L3(0xbf),
2760 L3(0xc0), L3(0xc1), L3(0xc2), L3(0xc3), L3(0xc4), L3(0xc5), L3(0xc6), L3(0xc7),
2761 L3(0xc8), L3(0xc9), L3(0xca), L3(0xcb), L3(0xcc), L3(0xcd), L3(0xce), L3(0xcf),
2762 L3(0xd0), L3(0xd1), L3(0xd2), L3(0xd3), L3(0xd4), L3(0xd5), L3(0xd6), L3(0xd7),
2763 L3(0xd8), L3(0xd9), L3(0xda), L3(0xdb), L3(0xdc), L3(0xdd), L3(0xde), L3(0xdf),
2764 L3(0xe0), L3(0xe1), L3(0xe2), L3(0xe3), L3(0xe4), L3(0xe5), L3(0xe6), L3(0xe7),
2765 L3(0xe8), L3(0xe9), L3(0xea), L3(0xeb), L3(0xec), L3(0xed), L3(0xee), L3(0xef),
2766 L3(0xf0), L3(0xf1), L3(0xf2), L3(0xf3), L3(0xf4), L3(0xf5), L3(0xf6), L3(0xf7),
2767 L3(0xf8), L3(0xf9), L3(0xfa), L3(0xfb), L3(0xfc), L3(0xfd), L3(0xfe), L3(0xff)
2768 };
2769
2770 #ifdef __SUNPRO_CC
2771 #pragma pack(0)
2772 #else
2773 #pragma pack(pop)
2774 #endif
2775
2776 #undef L1
2777 #undef L2
2778 #undef L3
2779
2780 static const char AsciiHundreds[] = {
2781 O10(0x30), O10(0x31), O10(0x32), O10(0x33), O10(0x34), O10(0x35), O10(0x36), O10(0x37), O10(0x38), O10(0x39),
2782 O11(0x30), O11(0x31), O11(0x32), O11(0x33), O11(0x34), O11(0x35), O11(0x36), O11(0x37), O11(0x38), O11(0x39),
2783 O12(0x30), O12(0x31), O12(0x32), O12(0x33), O12(0x34), O12(0x35), O12(0x36), O12(0x37), O12(0x38), O12(0x39),
2784 O13(0x30), O13(0x31), O13(0x32), O13(0x33), O13(0x34), O13(0x35), O13(0x36), O13(0x37), O13(0x38), O13(0x39),
2785 O14(0x30), O14(0x31), O14(0x32), O14(0x33), O14(0x34), O14(0x35), O14(0x36), O14(0x37), O14(0x38), O14(0x39),
2786 O15(0x30), O15(0x31), O15(0x32), O15(0x33), O15(0x34), O15(0x35), O15(0x36), O15(0x37), O15(0x38), O15(0x39),
2787 O16(0x30), O16(0x31), O16(0x32), O16(0x33), O16(0x34), O16(0x35), O16(0x36), O16(0x37), O16(0x38), O16(0x39),
2788 O17(0x30), O17(0x31), O17(0x32), O17(0x33), O17(0x34), O17(0x35), O17(0x36), O17(0x37), O17(0x38), O17(0x39),
2789 O18(0x30), O18(0x31), O18(0x32), O18(0x33), O18(0x34), O18(0x35), O18(0x36), O18(0x37), O18(0x38), O18(0x39),
2790 O19(0x30), O19(0x31), O19(0x32), O19(0x33), O19(0x34), O19(0x35), O19(0x36), O19(0x37), O19(0x38), O19(0x39),
2791 O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39),
2792 O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39),
2793 O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39),
2794 O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23