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

Annotation of /trunk/js/jsstr.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 siliconforks 507 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 siliconforks 399 * vim: set ts=8 sw=4 et tw=99:
3 siliconforks 332 *
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 siliconforks 507 #define __STDC_LIMIT_MACROS
52    
53 siliconforks 332 #include <stdlib.h>
54     #include <string.h>
55     #include "jstypes.h"
56 siliconforks 507 #include "jsstdint.h"
57 siliconforks 332 #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 siliconforks 399 #include "jsbuiltins.h"
65 siliconforks 332 #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 siliconforks 460 #include "jsstaticcheck.h"
76 siliconforks 332 #include "jsstr.h"
77     #include "jsbit.h"
78 siliconforks 507 #include "jsvector.h"
79     #include "jsstrinlines.h"
80 siliconforks 332
81     #define JSSTRDEP_RECURSION_LIMIT 100
82    
83 siliconforks 507 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 siliconforks 332 {
89     JSString *base;
90     size_t start, length;
91    
92 siliconforks 507 JS_ASSERT(str->isDependent());
93     base = str->dependentBase();
94     start = str->dependentStart();
95     if (base->isDependent()) {
96 siliconforks 332 if (level < JSSTRDEP_RECURSION_LIMIT) {
97 siliconforks 507 start += MinimizeDependentStrings(base, level + 1, &base);
98 siliconforks 332 } else {
99     do {
100 siliconforks 507 start += base->dependentStart();
101     base = base->dependentBase();
102     } while (base->isDependent());
103 siliconforks 332 }
104     if (start == 0) {
105 siliconforks 507 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 siliconforks 332 }
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 siliconforks 507 start = MinimizeDependentStrings(str, 0, &base);
123     JS_ASSERT(start < base->flatLength());
124     return base->flatChars() + start;
125 siliconforks 332 }
126    
127     const jschar *
128     js_GetStringChars(JSContext *cx, JSString *str)
129     {
130     if (!js_MakeStringImmutable(cx, str))
131     return NULL;
132 siliconforks 507 return str->flatChars();
133 siliconforks 332 }
134    
135     JSString * JS_FASTCALL
136     js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
137     {
138     size_t rn, ln, lrdist, n;
139 siliconforks 507 jschar *ls, *s;
140     const jschar *rs;
141 siliconforks 332 JSString *ldep; /* non-null if left should become dependent */
142     JSString *str;
143    
144 siliconforks 507 right->getCharsAndLength(rs, rn);
145 siliconforks 332 if (rn == 0)
146     return left;
147    
148 siliconforks 507 left->getCharsAndLength(const_cast<const jschar *&>(ls), ln);
149 siliconforks 332 if (ln == 0)
150     return right;
151    
152 siliconforks 507 if (!left->isMutable()) {
153 siliconforks 332 /* We must copy if left does not own a buffer to realloc. */
154 siliconforks 507 s = (jschar *) cx->malloc((ln + rn + 1) * sizeof(jschar));
155 siliconforks 332 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 siliconforks 507 JS_ASSERT(left->isFlat());
162     s = (jschar *) cx->realloc(ls, (ln + rn + 1) * sizeof(jschar));
163 siliconforks 332 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 siliconforks 507 left->mChars = ls = s;
171 siliconforks 332 ldep = left;
172     }
173    
174     js_strncpy(s + ln, rs, rn);
175     n = ln + rn;
176     s[n] = 0;
177 siliconforks 460
178 siliconforks 332 str = js_NewString(cx, s, n);
179     if (!str) {
180     /* Out of memory: clean up any space we (re-)allocated. */
181     if (!ldep) {
182 siliconforks 507 cx->free(s);
183 siliconforks 332 } else {
184 siliconforks 507 s = (jschar *) cx->realloc(ls, (ln + 1) * sizeof(jschar));
185 siliconforks 332 if (s)
186 siliconforks 507 left->mChars = s;
187 siliconforks 332 }
188     } else {
189 siliconforks 507 str->flatSetMutable();
190 siliconforks 332
191     /* Morph left into a dependent prefix if we realloc'd its buffer. */
192     if (ldep) {
193 siliconforks 507 ldep->reinitPrefix(str, ln);
194 siliconforks 332 #ifdef DEBUG
195 siliconforks 507 {
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 siliconforks 332 #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 siliconforks 507 if (str->isDependent()) {
217     n = str->dependentLength();
218 siliconforks 332 size = (n + 1) * sizeof(jschar);
219 siliconforks 507 s = (jschar *) cx->malloc(size);
220 siliconforks 332 if (!s)
221     return NULL;
222    
223 siliconforks 507 js_strncpy(s, str->dependentChars(), n);
224 siliconforks 332 s[n] = 0;
225 siliconforks 507 str->reinitFlat(s, n);
226 siliconforks 332
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 siliconforks 507 return str->flatChars();
240 siliconforks 332 }
241    
242     JSBool
243     js_MakeStringImmutable(JSContext *cx, JSString *str)
244     {
245 siliconforks 507 if (str->isDependent() && !js_UndependString(cx, str)) {
246 siliconforks 332 JS_RUNTIME_METER(cx->runtime, badUndependStrings);
247     return JS_FALSE;
248     }
249 siliconforks 507 str->flatClearMutable();
250 siliconforks 332 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 siliconforks 507 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 siliconforks 332 } else if (JSVAL_IS_BOOLEAN(*vp)) {
267 siliconforks 507 str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
268 siliconforks 332 JSVAL_TO_BOOLEAN(*vp)? 1 : 0]);
269 siliconforks 507 } 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 siliconforks 332 }
274 siliconforks 507 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 siliconforks 332 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 siliconforks 507 static const uint32 OVERLONG_UTF8 = UINT32_MAX;
303    
304 siliconforks 332 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 siliconforks 507 str->getCharsAndLength(chars, length);
382 siliconforks 332 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 siliconforks 507 newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
412 siliconforks 332 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 siliconforks 507 cx->free(newchars);
440 siliconforks 332 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 siliconforks 507 str->getCharsAndLength(chars, length);
471 siliconforks 332
472     /* Don't bother allocating less space for the new string. */
473 siliconforks 507 newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
474 siliconforks 332 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 siliconforks 507 cx->free(newchars);
503 siliconforks 332 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 siliconforks 460 if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
557 siliconforks 332 if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) {
558     /* Follow ECMA-262 by fetching intrinsic length of our string. */
559 siliconforks 507 v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
560 siliconforks 332 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 siliconforks 507 *vp = INT_TO_JSVAL((jsint) str->length());
570 siliconforks 332 }
571 siliconforks 460
572 siliconforks 332 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 siliconforks 507 v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
585 siliconforks 332 JS_ASSERT(JSVAL_IS_STRING(v));
586     str = JSVAL_TO_STRING(v);
587    
588 siliconforks 507 length = str->length();
589 siliconforks 332 for (i = 0; i < length; i++) {
590     str1 = js_NewDependentString(cx, str, i, 1);
591     if (!str1)
592     return JS_FALSE;
593 siliconforks 507 if (!obj->defineProperty(cx, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL,
594     STRING_ELEMENT_ATTRS)) {
595 siliconforks 332 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 siliconforks 507 v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
613 siliconforks 332 JS_ASSERT(JSVAL_IS_STRING(v));
614     str = JSVAL_TO_STRING(v);
615    
616     slot = JSVAL_TO_INT(id);
617 siliconforks 507 if ((size_t)slot < str->length()) {
618     str1 = JSString::getUnitString(cx, str, size_t(slot));
619 siliconforks 332 if (!str1)
620     return JS_FALSE;
621 siliconforks 507 if (!obj->defineProperty(cx, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL,
622     STRING_ELEMENT_ATTRS)) {
623 siliconforks 332 return JS_FALSE;
624     }
625     *objp = obj;
626     }
627     return JS_TRUE;
628     }
629    
630     JSClass js_StringClass = {
631     js_String_str,
632 siliconforks 507 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
633 siliconforks 332 JSCLASS_HAS_CACHED_PROTO(JSProto_String),
634     JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub,
635 siliconforks 507 str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, NULL,
636 siliconforks 332 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 siliconforks 507 const jschar *s;
691     jschar *t;
692 siliconforks 332
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 siliconforks 507 str->getCharsAndLength(s, k);
701 siliconforks 332 n = j + k + 2;
702 siliconforks 507 t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
703 siliconforks 332 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 siliconforks 507 cx->free(t);
715 siliconforks 332 return JS_FALSE;
716     }
717     *vp = STRING_TO_JSVAL(str);
718     return JS_TRUE;
719     }
720    
721     #endif /* JS_HAS_TOSOURCE */
722    
723 siliconforks 507 JSBool
724     js_str_toString(JSContext *cx, uintN argc, jsval *vp)
725 siliconforks 332 {
726     return js_GetPrimitiveThis(cx, vp, &js_StringClass, vp);
727     }
728    
729     /*
730     * Java-like string native methods.
731     */
732 siliconforks 399
733     static JSString *
734     SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end)
735 siliconforks 332 {
736 siliconforks 399 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 siliconforks 332 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 siliconforks 507 length = str->length();
768 siliconforks 332 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 siliconforks 399 str = SubstringTail(cx, str, length, begin, end);
779 siliconforks 332 if (!str)
780     return JS_FALSE;
781     }
782     *vp = STRING_TO_JSVAL(str);
783     return JS_TRUE;
784     }
785    
786 siliconforks 399 #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 siliconforks 507 jsval v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
793 siliconforks 399 JS_ASSERT(JSVAL_IS_STRING(v));
794     return JSVAL_TO_STRING(v);
795     }
796     #endif
797    
798 siliconforks 332 JSString* JS_FASTCALL
799     js_toLowerCase(JSContext *cx, JSString *str)
800     {
801     size_t i, n;
802 siliconforks 507 const jschar *s;
803     jschar *news;
804 siliconforks 332
805 siliconforks 507 str->getCharsAndLength(s, n);
806     news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
807 siliconforks 332 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 siliconforks 507 cx->free(news);
815 siliconforks 332 return NULL;
816     }
817     return str;
818     }
819    
820 siliconforks 399 static JSBool
821     str_toLowerCase(JSContext *cx, uintN argc, jsval *vp)
822 siliconforks 332 {
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 siliconforks 399 return str_toLowerCase(cx, 0, vp);
847 siliconforks 332 }
848    
849     JSString* JS_FASTCALL
850     js_toUpperCase(JSContext *cx, JSString *str)
851     {
852     size_t i, n;
853 siliconforks 507 const jschar *s;
854     jschar *news;
855 siliconforks 332
856 siliconforks 507 str->getCharsAndLength(s, n);
857     news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
858 siliconforks 332 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 siliconforks 507 cx->free(news);
866 siliconforks 332 return NULL;
867     }
868     return str;
869     }
870    
871 siliconforks 399 static JSBool
872     str_toUpperCase(JSContext *cx, uintN argc, jsval *vp)
873 siliconforks 332 {
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 siliconforks 399 return str_toUpperCase(cx, 0, vp);
898 siliconforks 332 }
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 siliconforks 399 static JSBool
922     str_charAt(JSContext *cx, uintN argc, jsval *vp)
923 siliconforks 332 {
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 siliconforks 507 if ((size_t)i >= str->length())
934 siliconforks 332 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 siliconforks 507 if (d < 0 || str->length() <= d)
950 siliconforks 332 goto out_of_range;
951     i = (jsint) d;
952     }
953    
954 siliconforks 507 str = JSString::getUnitString(cx, str, size_t(i));
955 siliconforks 332 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 siliconforks 399 static JSBool
966     str_charCodeAt(JSContext *cx, uintN argc, jsval *vp)
967 siliconforks 332 {
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 siliconforks 507 if ((size_t)i >= str->length())
978 siliconforks 332 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 siliconforks 507 if (d < 0 || str->length() <= d)
994 siliconforks 332 goto out_of_range;
995     i = (jsint) d;
996     }
997    
998 siliconforks 507 *vp = INT_TO_JSVAL(str->chars()[i]);
999 siliconforks 332 return JS_TRUE;
1000    
1001     out_of_range:
1002     *vp = JS_GetNaNValue(cx);
1003     return JS_TRUE;
1004     }
1005    
1006 siliconforks 399 #ifdef JS_TRACER
1007 siliconforks 460 extern jsdouble js_NaN;
1008    
1009     jsdouble FASTCALL
1010     js_String_p_charCodeAt(JSString* str, jsdouble d)
1011     {
1012     d = js_DoubleToInteger(d);
1013 siliconforks 507 if (d < 0 || (int32)str->length() <= d)
1014 siliconforks 460 return js_NaN;
1015 siliconforks 507 return jsdouble(str->chars()[jsuint(d)]);
1016 siliconforks 460 }
1017    
1018 siliconforks 399 int32 FASTCALL
1019 siliconforks 460 js_String_p_charCodeAt_int(JSString* str, jsint i)
1020 siliconforks 399 {
1021 siliconforks 507 if (i < 0 || (int32)str->length() <= i)
1022 siliconforks 460 return 0;
1023 siliconforks 507 return str->chars()[i];
1024 siliconforks 399 }
1025 siliconforks 507 JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int, STRING, INT32, 1, 1)
1026 siliconforks 460
1027     jsdouble FASTCALL
1028     js_String_p_charCodeAt0(JSString* str)
1029     {
1030 siliconforks 507 if ((int32)str->length() == 0)
1031 siliconforks 460 return js_NaN;
1032 siliconforks 507 return jsdouble(str->chars()[0]);
1033 siliconforks 460 }
1034    
1035 siliconforks 507 /*
1036     * The FuncFilter replaces the generic double version of charCodeAt with the
1037     * integer fast path if appropriate.
1038     */
1039 siliconforks 460 int32 FASTCALL
1040     js_String_p_charCodeAt0_int(JSString* str)
1041     {
1042 siliconforks 507 if ((int32)str->length() == 0)
1043 siliconforks 460 return 0;
1044 siliconforks 507 return str->chars()[0];
1045 siliconforks 460 }
1046     JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING, 1, 1)
1047 siliconforks 399 #endif
1048    
1049 siliconforks 332 jsint
1050 siliconforks 507 js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1051     const jschar *pat, jsuint patlen)
1052 siliconforks 332 {
1053 siliconforks 507 uint8 skip[sBMHCharSetSize];
1054 siliconforks 332
1055 siliconforks 507 JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1056     for (jsuint i = 0; i < sBMHCharSetSize; i++)
1057 siliconforks 332 skip[i] = (uint8)patlen;
1058 siliconforks 507 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 siliconforks 332 skip[c] = (uint8)(m - i);
1064     }
1065 siliconforks 507 jschar c;
1066     for (jsuint k = m;
1067 siliconforks 332 k < textlen;
1068 siliconforks 507 k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1069     for (jsuint i = k, j = m; ; i--, j--) {
1070 siliconforks 332 if (text[i] != pat[j])
1071     break;
1072 siliconforks 507 if (j == 0)
1073     return static_cast<jsint>(i); /* safe: max string size */
1074 siliconforks 332 }
1075     }
1076     return -1;
1077     }
1078    
1079 siliconforks 507 static JS_ALWAYS_INLINE jsint
1080     StringMatch(const jschar *text, jsuint textlen,
1081     const jschar *pat, jsuint patlen)
1082 siliconforks 332 {
1083 siliconforks 507 if (patlen == 0)
1084     return 0;
1085     if (textlen < patlen)
1086     return -1;
1087 siliconforks 332
1088 siliconforks 507 #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 siliconforks 332
1103 siliconforks 507 /*
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 siliconforks 332 }
1113    
1114 siliconforks 507 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 siliconforks 332
1120 siliconforks 507 #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 siliconforks 332
1130 siliconforks 507 /* 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 siliconforks 332 }
1157 siliconforks 507 return -1;
1158     }
1159 siliconforks 332
1160 siliconforks 507 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 siliconforks 332 }
1191     } else {
1192 siliconforks 507 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 siliconforks 332 }
1207 siliconforks 507 } else {
1208     start = 0;
1209 siliconforks 332 }
1210    
1211 siliconforks 507 jsint match = StringMatch(text, textlen, pat, patlen);
1212     *vp = INT_TO_JSVAL((match == -1) ? -1 : start + match);
1213     return true;
1214 siliconforks 332 }
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 siliconforks 507 text = str->chars();
1226     textlen = (jsint) str->length();
1227 siliconforks 332
1228 siliconforks 460 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 siliconforks 507 pat = str2->chars();
1236     patlen = (jsint) str2->length();
1237 siliconforks 332
1238 siliconforks 460 i = textlen - patlen; // Start searching here
1239     if (i < 0) {
1240     *vp = INT_TO_JSVAL(-1);
1241     return JS_TRUE;
1242     }
1243    
1244 siliconforks 332 if (argc > 1) {
1245 siliconforks 460 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 siliconforks 332 } else {
1252 siliconforks 460 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 siliconforks 332 }
1263     }
1264    
1265     if (patlen == 0) {
1266     *vp = INT_TO_JSVAL(i);
1267     return JS_TRUE;
1268     }
1269    
1270 siliconforks 507 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 siliconforks 332 }
1286 siliconforks 507 break_continue:;
1287 siliconforks 332 }
1288 siliconforks 507
1289     *vp = INT_TO_JSVAL(-1);
1290 siliconforks 332 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 siliconforks 507 str->getCharsAndLength(chars, length);
1302 siliconforks 332 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 siliconforks 507 * 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 siliconforks 332 */
1356 siliconforks 507 class RegExpGuard
1357 siliconforks 332 {
1358 siliconforks 507 RegExpGuard(const RegExpGuard &);
1359     void operator=(const RegExpGuard &);
1360 siliconforks 332
1361 siliconforks 507 JSContext *mCx;
1362     JSObject *mReobj;
1363     JSRegExp *mRe;
1364 siliconforks 332
1365 siliconforks 507 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 siliconforks 332 } else {
1383 siliconforks 507 patstr = ArgToRootedString(mCx, argc, vp, 0);
1384     if (!patstr)
1385     return false;
1386 siliconforks 332 }
1387 siliconforks 507 return true;
1388 siliconforks 332 }
1389    
1390 siliconforks 507 /*
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 siliconforks 332 }
1411 siliconforks 507 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 siliconforks 332 } else {
1437 siliconforks 507 opt = NULL;
1438 siliconforks 332 }
1439 siliconforks 507 mRe = js_NewRegExpOpt(mCx, patstr, opt, flat);
1440     if (!mRe)
1441     return false;
1442     mReobj = NULL;
1443     return true;
1444 siliconforks 332 }
1445    
1446 siliconforks 507 /* Data available on successful return from |normalizeRegExp|. */
1447     JSObject *reobj() const { return mReobj; } /* nullable */
1448     JSRegExp *re() const { return mRe; } /* non-null */
1449     };
1450 siliconforks 332
1451 siliconforks 507 /* 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 siliconforks 332 }
1457    
1458 siliconforks 507 typedef bool (*DoMatchCallback)(JSContext *cx, size_t count, void *data);
1459 siliconforks 332
1460 siliconforks 507 /*
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 siliconforks 332 {
1479 siliconforks 507 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 siliconforks 332
1509 siliconforks 507 /*
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 siliconforks 332 if (!arrayobj) {
1521 siliconforks 460 arrayobj = js_NewArrayObject(cx, 0, NULL);
1522 siliconforks 332 if (!arrayobj)
1523 siliconforks 507 return false;
1524     arrayval = OBJECT_TO_JSVAL(arrayobj);
1525 siliconforks 332 }
1526 siliconforks 507
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 siliconforks 332 if (!matchstr)
1533 siliconforks 507 return false;
1534 siliconforks 399
1535 siliconforks 507 jsval v = STRING_TO_JSVAL(matchstr);
1536    
1537 siliconforks 399 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1538 siliconforks 507 return arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
1539 siliconforks 332 }
1540    
1541 siliconforks 507 static bool
1542     BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g,
1543     jsval *vp)
1544 siliconforks 332 {
1545 siliconforks 507 if (g.match < 0) {
1546     *vp = JSVAL_NULL;
1547     return true;
1548     }
1549 siliconforks 332
1550 siliconforks 507 /* 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 siliconforks 332 }
1562    
1563 siliconforks 399 static JSBool
1564     str_match(JSContext *cx, uintN argc, jsval *vp)
1565 siliconforks 332 {
1566 siliconforks 507 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 siliconforks 332 }
1586    
1587     static JSBool
1588     str_search(JSContext *cx, uintN argc, jsval *vp)
1589     {
1590 siliconforks 507 JSString *str;
1591     NORMALIZE_THIS(cx, vp, str);
1592 siliconforks 332
1593 siliconforks 507 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 siliconforks 332 }
1613    
1614 siliconforks 507 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 siliconforks 332
1629     static JSSubString *
1630 siliconforks 507 InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
1631     size_t *skip)
1632 siliconforks 332 {
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 siliconforks 507 rdata.dollarStr.chars = dp;
1673     rdata.dollarStr.length = 1;
1674     return &rdata.dollarStr;
1675 siliconforks 332 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 siliconforks 507 static JS_ALWAYS_INLINE bool
1688     PushRegExpSubstr(JSContext *cx, const JSSubString &sub, jsval *&sp)
1689 siliconforks 332 {
1690 siliconforks 507 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 siliconforks 332 JSString *repstr;
1703     size_t replen, skip;
1704     jschar *dp, *ep;
1705     JSSubString *sub;
1706     JSObject *lambda;
1707    
1708 siliconforks 507 lambda = rdata.lambda;
1709 siliconforks 332 if (lambda) {
1710 siliconforks 507 uintN i, m, n;
1711 siliconforks 332
1712 siliconforks 507 js_LeaveTrace(cx);
1713 siliconforks 332
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 siliconforks 507 * DoReplace. The lambda is called with arguments ($&, $1, $2, ...,
1718 siliconforks 332 * 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 siliconforks 507 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 siliconforks 332 if (!invokevp)
1727 siliconforks 507 return false;
1728 siliconforks 332
1729 siliconforks 507 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 siliconforks 332 /* Push lambda and its 'this' parameter. */
1742 siliconforks 507 jsval *sp = invokevp;
1743 siliconforks 332 *sp++ = OBJECT_TO_JSVAL(lambda);
1744     *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));
1745    
1746 siliconforks 507 /* Push $&, $1, $2, ... */
1747     if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
1748     goto lambda_out;
1749 siliconforks 332
1750     i = 0;
1751     m = cx->regExpStatics.parenCount;
1752     n = JS_MIN(m, 9);
1753 siliconforks 507 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 siliconforks 332
1762     /*
1763     * We need to clear moreParens in the top-of-stack cx->regExpStatics
1764 siliconforks 507 * so it won't be possibly realloc'ed, leaving the bottom-of-stack
1765 siliconforks 332 * moreParens pointing to freed memory.
1766     */
1767     cx->regExpStatics.moreParens = NULL;
1768 siliconforks 507 freeMoreParens = true;
1769 siliconforks 332
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 siliconforks 507 *sp++ = STRING_TO_JSVAL(rdata.str);
1777 siliconforks 332
1778 siliconforks 507 if (!js_Invoke(cx, argc, invokevp, 0))
1779     goto lambda_out;
1780 siliconforks 332
1781 siliconforks 507 /*
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 siliconforks 332 lambda_out:
1796     js_FreeStack(cx, mark);
1797     if (freeMoreParens)
1798 siliconforks 507 cx->free(cx->regExpStatics.moreParens);
1799 siliconforks 332 cx->regExpStatics = save;
1800     return ok;
1801     }
1802    
1803 siliconforks 507 repstr = rdata.repstr;
1804     replen = repstr->length();
1805     for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
1806 siliconforks 332 dp = js_strchr_limit(dp, '$', ep)) {
1807 siliconforks 507 sub = InterpretDollar(cx, dp, ep, rdata, &skip);
1808 siliconforks 332 if (sub) {
1809     replen += sub->length - skip;
1810     dp += skip;
1811     }
1812     else
1813     dp++;
1814     }
1815     *sizep = replen;
1816 siliconforks 507 return true;
1817 siliconforks 332 }
1818    
1819     static void
1820 siliconforks 507 DoReplace(JSContext *cx, ReplaceData &rdata, jschar *chars)
1821 siliconforks 332 {
1822     JSString *repstr;
1823     jschar *bp, *cp, *dp, *ep;
1824     size_t len, skip;
1825     JSSubString *sub;
1826    
1827 siliconforks 507 repstr = rdata.repstr;
1828     bp = cp = repstr->chars();
1829     for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
1830 siliconforks 332 dp = js_strchr_limit(dp, '$', ep)) {
1831     len = dp - cp;
1832     js_strncpy(chars, cp, len);
1833     chars += len;
1834     cp = dp;
1835 siliconforks 507 sub = InterpretDollar(cx, dp, ep, rdata, &skip);
1836 siliconforks 332 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 siliconforks 507 js_strncpy(chars, cp, repstr->length() - (cp - bp));
1847 siliconforks 332 }
1848    
1849 siliconforks 507 static bool
1850     ReplaceCallback(JSContext *cx, size_t count, void *p)
1851 siliconforks 332 {
1852 siliconforks 507 ReplaceData &rdata = *static_cast<ReplaceData *>(p);
1853 siliconforks 332
1854 siliconforks 507 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 siliconforks 332
1862 siliconforks 507 size_t replen = 0; /* silence 'unused' warning */
1863     if (!FindReplaceLength(cx, rdata, &replen))
1864     return false;
1865 siliconforks 332
1866 siliconforks 507 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 siliconforks 332 js_strncpy(chars, left, leftlen);
1873     chars += leftlen;
1874 siliconforks 507 DoReplace(cx, rdata, chars);
1875     return true;
1876 siliconforks 332 }
1877    
1878 siliconforks 507 static bool
1879     BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
1880     const RegExpGuard &g, jsval *vp)
1881 siliconforks 332 {
1882 siliconforks 507 if (g.match == -1) {
1883     *vp = STRING_TO_JSVAL(textstr);
1884     return true;
1885     }
1886 siliconforks 332
1887 siliconforks 507 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 siliconforks 332 }
1898    
1899 siliconforks 507 JSString *str = js_NewStringFromCharBuffer(cx, cb);
1900     if (!str)
1901     return false;
1902     *vp = STRING_TO_JSVAL(str);
1903     return true;
1904 siliconforks 332 }
1905    
1906 siliconforks 507 static JSBool
1907     str_replace(JSContext *cx, uintN argc, jsval *vp)
1908 siliconforks 332 {
1909 siliconforks 507 ReplaceData rdata(cx);
1910     NORMALIZE_THIS(cx, vp, rdata.str);
1911 siliconforks 332
1912 siliconforks 507 /* 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 siliconforks 332
1923 siliconforks 507 /* 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 siliconforks 332 rdata.dollarEnd);
1929     }
1930 siliconforks 507
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 siliconforks 332 rdata.index = 0;
1941     rdata.leftIndex = 0;
1942 siliconforks 507 rdata.calledBack = false;
1943 siliconforks 332
1944 siliconforks 507 if (!DoMatch(cx, vp, rdata.str, rdata.g, ReplaceCallback, &rdata, REPLACE_ARGS))
1945     return false;
1946 siliconforks 332
1947 siliconforks 507 if (!rdata.calledBack) {
1948     /* Didn't match, so the string is unmodified. */
1949     *vp = STRING_TO_JSVAL(rdata.str);
1950     return true;
1951 siliconforks 332 }
1952    
1953 siliconforks 507 JSSubString *sub = &cx->regExpStatics.rightContext;
1954     if (!rdata.cb.append(sub->chars, sub->length))
1955     return false;
1956 siliconforks 332
1957 siliconforks 507 JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
1958     if (!retstr)
1959     return false;
1960 siliconforks 332
1961 siliconforks 507 *vp = STRING_TO_JSVAL(retstr);
1962     return true;
1963 siliconforks 332 }
1964    
1965     /*
1966 siliconforks 399 * Subroutine used by str_split to find the next split point in str, starting
1967 siliconforks 332 * 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 siliconforks 399 * limit argument (see str_split).
1992 siliconforks 332 */
1993     i = *ip;
1994 siliconforks 507 length = str->length();
1995 siliconforks 332 if ((size_t)i > length)
1996     return -1;
1997    
1998 siliconforks 507 chars = str->chars();
1999 siliconforks 332
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 siliconforks 507 * in DoMatch.
2026 siliconforks 332 */
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 siliconforks 399 static JSBool
2078     str_split(JSContext *cx, uintN argc, jsval *vp)
2079 siliconforks 332 {
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 siliconforks 460 arrayobj = js_NewArrayObject(cx, 0, NULL);
2093 siliconforks 332 if (!arrayobj)
2094     return JS_FALSE;
2095     *vp = OBJECT_TO_JSVAL(arrayobj);
2096    
2097     if (argc == 0) {
2098     v = STRING_TO_JSVAL(str);
2099 siliconforks 507 ok = arrayobj->setProperty(cx, INT_TO_JSID(0), &v);
2100 siliconforks 332 } else {
2101     if (VALUE_IS_REGEXP(cx, vp[2])) {
2102 siliconforks 507 re = (JSRegExp *) JSVAL_TO_OBJECT(vp[2])->getPrivate();
2103 siliconforks 332 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 siliconforks 507 str2->getCharsAndLength(tmp.chars, tmp.length);
2119 siliconforks 332 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 siliconforks 507 if (limit > str->length())
2134     limit = 1 + str->length();
2135 siliconforks 332 }
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 siliconforks 507 length = str->length();
2193 siliconforks 332 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 siliconforks 399 static JSBool
2231     str_concat(JSContext *cx, uintN argc, jsval *vp)
2232 siliconforks 332 {
2233     JSString *str, *str2;
2234     jsval *argv;
2235     uintN i;
2236    
2237     NORMALIZE_THIS(cx, vp, str);
2238    
2239 siliconforks 460 /* Set vp (aka rval) early to handle the argc == 0 case. */
2240     *vp = STRING_TO_JSVAL(str);
2241    
2242 siliconforks 332 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 siliconforks 460 *vp = STRING_TO_JSVAL(str);
2252 siliconforks 332 }
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 siliconforks 507 end = str->length();
2271 siliconforks 332 if (begin <= end) {
2272     length = end - begin;
2273     if (length == 0) {
2274     str = cx->runtime->emptyString;
2275     } else {
2276     str = (length == 1)
2277 siliconforks 507 ? JSString::getUnitString(cx, str, begin)
2278 siliconforks 332 : 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 siliconforks 507 length = str->length();
2297 siliconforks 332 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 siliconforks 507 parlen = param->length();
2356 siliconforks 332 taglen += 2 + parlen + 1; /* '="param"' */
2357     }
2358     endlen = strlen(end);
2359 siliconforks 507 taglen += str->length() + 2 + endlen + 1; /* 'str</end>' */
2360 siliconforks 332
2361     if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2362     js_ReportAllocationOverflow(cx);
2363     return JS_FALSE;
2364     }
2365    
2366 siliconforks 507 tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2367 siliconforks 332 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 siliconforks 507 js_strncpy(&tagbuf[j], param->chars(), parlen);
2378 siliconforks 332 j += parlen;
2379     tagbuf[j++] = '"';
2380     }
2381     tagbuf[j++] = '>';
2382 siliconforks 507 js_strncpy(&tagbuf[j], str->chars(), str->length());
2383     j += str->length();
2384 siliconforks 332 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 siliconforks 507 js_free((char *)tagbuf);
2395 siliconforks 332 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     {