LCOV - app.info - js/src/jsstr.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsstr.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 2131 | 1341 | 62.9 %
| Date: | 2012-04-07 | Functions: | 138 | 107 | 77.5 %

           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                 :
          52                 : #include "mozilla/Attributes.h"
          53                 :
          54                 : #include <stdlib.h>
          55                 : #include <string.h>
          56                 : #include "jstypes.h"
          57                 : #include "jsutil.h"
          58                 : #include "jshash.h"
          59                 : #include "jsprf.h"
          60                 : #include "jsapi.h"
          61                 : #include "jsarray.h"
          62                 : #include "jsatom.h"
          63                 : #include "jsbool.h"
          64                 : #include "jscntxt.h"
          65                 : #include "jsgc.h"
          66                 : #include "jsinterp.h"
          67                 : #include "jslock.h"
          68                 : #include "jsnum.h"
          69                 : #include "jsobj.h"
          70                 : #include "jsopcode.h"
          71                 : #include "jsprobes.h"
          72                 : #include "jsscope.h"
          73                 : #include "jsstr.h"
          74                 : #include "jsversion.h"
          75                 :
          76                 : #include "builtin/RegExp.h"
          77                 : #include "vm/GlobalObject.h"
          78                 : #include "vm/RegExpObject.h"
          79                 : #include "vm/StringBuffer.h"
          80                 :
          81                 : #include "jsinferinlines.h"
          82                 : #include "jsobjinlines.h"
          83                 : #include "jsstrinlines.h"
          84                 : #include "jsautooplen.h"        // generated headers last
          85                 :
          86                 : #include "vm/MethodGuard-inl.h"
          87                 : #include "vm/RegExpObject-inl.h"
          88                 : #include "vm/RegExpStatics-inl.h"
          89                 : #include "vm/StringObject-inl.h"
          90                 : #include "vm/String-inl.h"
          91                 :
          92                 : using namespace js;
          93                 : using namespace js::gc;
          94                 : using namespace js::types;
          95                 : using namespace js::unicode;
          96                 :
          97                 : static JSLinearString *
          98          920970 : ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno)
          99                 : {
         100          920970 :     if (argno >= args.length())
         101              27 :         return cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
         102                 :
         103          920943 :     Value &arg = args[argno];
         104          920943 :     JSString *str = ToString(cx, arg);
         105          920943 :     if (!str)
         106              72 :         return NULL;
         107                 :
         108          920871 :     arg = StringValue(str);
         109          920871 :     return str->ensureLinear(cx);
         110                 : }
         111                 :
         112                 : /*
         113                 :  * Forward declarations for URI encode/decode and helper routines
         114                 :  */
         115                 : static JSBool
         116                 : str_decodeURI(JSContext *cx, unsigned argc, Value *vp);
         117                 :
         118                 : static JSBool
         119                 : str_decodeURI_Component(JSContext *cx, unsigned argc, Value *vp);
         120                 :
         121                 : static JSBool
         122                 : str_encodeURI(JSContext *cx, unsigned argc, Value *vp);
         123                 :
         124                 : static JSBool
         125                 : str_encodeURI_Component(JSContext *cx, unsigned argc, Value *vp);
         126                 :
         127                 : static const uint32_t INVALID_UTF8 = UINT32_MAX;
         128                 :
         129                 : static uint32_t
         130                 : Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length);
         131                 :
         132                 : /*
         133                 :  * Global string methods
         134                 :  */
         135                 :
         136                 :
         137                 : /* ES5 B.2.1 */
         138                 : static JSBool
         139              18 : str_escape(JSContext *cx, unsigned argc, Value *vp)
         140                 : {
         141              18 :     CallArgs args = CallArgsFromVp(argc, vp);
         142                 :
         143                 :     const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
         144              18 :                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
         145                 :
         146              18 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
         147              18 :     if (!str)
         148               0 :         return false;
         149                 :
         150              18 :     size_t length = str->length();
         151              18 :     const jschar *chars = str->chars();
         152                 :
         153                 :     static const uint8_t shouldPassThrough[256] = {
         154                 :          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
         155                 :          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
         156                 :          0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,       /*    !"#$%&'()*+,-./  */
         157                 :          1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,       /*   0123456789:;<=>?  */
         158                 :          1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,       /*   @ABCDEFGHIJKLMNO  */
         159                 :          1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,       /*   PQRSTUVWXYZ[\]^_  */
         160                 :          0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,       /*   `abcdefghijklmno  */
         161                 :          1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,     /*   pqrstuvwxyz{\}~  DEL */
         162                 :     };
         163                 :
         164                 :     /* In step 7, exactly 69 characters should pass through unencoded. */
         165                 : #ifdef DEBUG
         166              18 :     size_t count = 0;
         167            4626 :     for (size_t i = 0; i < sizeof(shouldPassThrough); i++) {
         168            4608 :         if (shouldPassThrough[i]) {
         169            1242 :             count++;
         170                 :         }
         171                 :     }
         172              18 :     JS_ASSERT(count == 69);
         173                 : #endif
         174                 :
         175                 :
         176                 :     /* Take a first pass and see how big the result string will need to be. */
         177              18 :     size_t newlength = length;
         178              36 :     for (size_t i = 0; i < length; i++) {
         179              18 :         jschar ch = chars[i];
         180              18 :         if (ch < 128 && shouldPassThrough[ch])
         181              18 :             continue;
         182                 :
         183                 :         /* The character will be encoded as %XX or %uXXXX. */
         184               0 :         newlength += (ch < 256) ? 2 : 5;
         185                 :
         186                 :         /*
         187                 :          * This overflow test works because newlength is incremented by at
         188                 :          * most 5 on each iteration.
         189                 :          */
         190               0 :         if (newlength < length) {
         191               0 :             js_ReportAllocationOverflow(cx);
         192               0 :             return false;
         193                 :         }
         194                 :     }
         195                 :
         196              18 :     if (newlength >= ~(size_t)0 / sizeof(jschar)) {
         197               0 :         js_ReportAllocationOverflow(cx);
         198               0 :         return false;
         199                 :     }
         200                 :
         201              18 :     jschar *newchars = (jschar *) cx->malloc_((newlength + 1) * sizeof(jschar));
         202              18 :     if (!newchars)
         203               0 :         return false;
         204                 :     size_t i, ni;
         205              36 :     for (i = 0, ni = 0; i < length; i++) {
         206              18 :         jschar ch = chars[i];
         207              18 :         if (ch < 128 && shouldPassThrough[ch]) {
         208              18 :             newchars[ni++] = ch;
         209               0 :         } else if (ch < 256) {
         210               0 :             newchars[ni++] = '%';
         211               0 :             newchars[ni++] = digits[ch >> 4];
         212               0 :             newchars[ni++] = digits[ch & 0xF];
         213                 :         } else {
         214               0 :             newchars[ni++] = '%';
         215               0 :             newchars[ni++] = 'u';
         216               0 :             newchars[ni++] = digits[ch >> 12];
         217               0 :             newchars[ni++] = digits[(ch & 0xF00) >> 8];
         218               0 :             newchars[ni++] = digits[(ch & 0xF0) >> 4];
         219               0 :             newchars[ni++] = digits[ch & 0xF];
         220                 :         }
         221                 :     }
         222              18 :     JS_ASSERT(ni == newlength);
         223              18 :     newchars[newlength] = 0;
         224                 :
         225              18 :     JSString *retstr = js_NewString(cx, newchars, newlength);
         226              18 :     if (!retstr) {
         227               0 :         cx->free_(newchars);
         228               0 :         return false;
         229                 :     }
         230                 :
         231              18 :     args.rval() = StringValue(retstr);
         232              18 :     return true;
         233                 : }
         234                 :
         235                 : static inline bool
         236               0 : Unhex4(const jschar *chars, jschar *result)
         237                 : {
         238               0 :     jschar a = chars[0],
         239               0 :            b = chars[1],
         240               0 :            c = chars[2],
         241               0 :            d = chars[3];
         242                 :
         243               0 :     if (!(JS7_ISHEX(a) && JS7_ISHEX(b) && JS7_ISHEX(c) && JS7_ISHEX(d)))
         244               0 :         return false;
         245                 :
         246               0 :     *result = (((((JS7_UNHEX(a) << 4) + JS7_UNHEX(b)) << 4) + JS7_UNHEX(c)) << 4) + JS7_UNHEX(d);
         247               0 :     return true;
         248                 : }
         249                 :
         250                 : static inline bool
         251               0 : Unhex2(const jschar *chars, jschar *result)
         252                 : {
         253               0 :     jschar a = chars[0],
         254               0 :            b = chars[1];
         255                 :
         256               0 :     if (!(JS7_ISHEX(a) && JS7_ISHEX(b)))
         257               0 :         return false;
         258                 :
         259               0 :     *result = (JS7_UNHEX(a) << 4) + JS7_UNHEX(b);
         260               0 :     return true;
         261                 : }
         262                 :
         263                 : /* ES5 B.2.2 */
         264                 : static JSBool
         265               0 : str_unescape(JSContext *cx, unsigned argc, Value *vp)
         266                 : {
         267               0 :     CallArgs args = CallArgsFromVp(argc, vp);
         268                 :
         269                 :     /* Step 1. */
         270               0 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
         271               0 :     if (!str)
         272               0 :         return false;
         273                 :
         274                 :     /* Step 2. */
         275               0 :     size_t length = str->length();
         276               0 :     const jschar *chars = str->chars();
         277                 :
         278                 :     /* Step 3. */
         279               0 :     StringBuffer sb(cx);
         280                 :
         281                 :     /*
         282                 :      * Note that the spec algorithm has been optimized to avoid building
         283                 :      * a string in the case where no escapes are present.
         284                 :      */
         285                 :
         286                 :     /* Step 4. */
         287               0 :     size_t k = 0;
         288               0 :     bool building = false;
         289                 :
         290               0 :     while (true) {
         291                 :         /* Step 5. */
         292               0 :         if (k == length) {
         293                 :             JSLinearString *result;
         294               0 :             if (building) {
         295               0 :                 result = sb.finishString();
         296               0 :                 if (!result)
         297               0 :                     return false;
         298                 :             } else {
         299               0 :                 result = str;
         300                 :             }
         301                 :
         302               0 :             args.rval() = StringValue(result);
         303               0 :             return true;
         304                 :         }
         305                 :
         306                 :         /* Step 6. */
         307               0 :         jschar c = chars[k];
         308                 :
         309                 :         /* Step 7. */
         310               0 :         if (c != '%')
         311               0 :             goto step_18;
         312                 :
         313                 :         /* Step 8. */
         314               0 :         if (k > length - 6)
         315               0 :             goto step_14;
         316                 :
         317                 :         /* Step 9. */
         318               0 :         if (chars[k + 1] != 'u')
         319               0 :             goto step_14;
         320                 :
         321                 : #define ENSURE_BUILDING                             \
         322                 :     JS_BEGIN_MACRO                                  \
         323                 :         if (!building) {                            \
         324                 :             building = true;                        \
         325                 :             if (!sb.reserve(length))                \
         326                 :                 return false;                       \
         327                 :             sb.infallibleAppend(chars, chars + k);  \
         328                 :         }                                           \
         329                 :     JS_END_MACRO
         330                 :
         331                 :         /* Step 10-13. */
         332               0 :         if (Unhex4(&chars[k + 2], &c)) {
         333               0 :             ENSURE_BUILDING;
         334               0 :             k += 5;
         335               0 :             goto step_18;
         336                 :         }
         337                 :
         338                 :       step_14:
         339                 :         /* Step 14. */
         340               0 :         if (k > length - 3)
         341               0 :             goto step_18;
         342                 :
         343                 :         /* Step 15-17. */
         344               0 :         if (Unhex2(&chars[k + 1], &c)) {
         345               0 :             ENSURE_BUILDING;
         346               0 :             k += 2;
         347                 :         }
         348                 :
         349                 :       step_18:
         350               0 :         if (building)
         351               0 :             sb.infallibleAppend(c);
         352                 :
         353                 :         /* Step 19. */
         354               0 :         k += 1;
         355                 :     }
         356                 : #undef ENSURE_BUILDING
         357                 : }
         358                 :
         359                 : #if JS_HAS_UNEVAL
         360                 : static JSBool
         361            5031 : str_uneval(JSContext *cx, unsigned argc, Value *vp)
         362                 : {
         363            5031 :     CallArgs args = CallArgsFromVp(argc, vp);
         364            5031 :     JSString *str = js_ValueToSource(cx, args.length() != 0 ? args[0] : UndefinedValue());
         365            5031 :     if (!str)
         366               0 :         return false;
         367                 :
         368            5031 :     args.rval() = StringValue(str);
         369            5031 :     return true;
         370                 : }
         371                 : #endif
         372                 :
         373                 : const char js_escape_str[] = "escape";
         374                 : const char js_unescape_str[] = "unescape";
         375                 : #if JS_HAS_UNEVAL
         376                 : const char js_uneval_str[] = "uneval";
         377                 : #endif
         378                 : const char js_decodeURI_str[] = "decodeURI";
         379                 : const char js_encodeURI_str[] = "encodeURI";
         380                 : const char js_decodeURIComponent_str[] = "decodeURIComponent";
         381                 : const char js_encodeURIComponent_str[] = "encodeURIComponent";
         382                 :
         383                 : static JSFunctionSpec string_functions[] = {
         384                 :     JS_FN(js_escape_str,             str_escape,                1,0),
         385                 :     JS_FN(js_unescape_str,           str_unescape,              1,0),
         386                 : #if JS_HAS_UNEVAL
         387                 :     JS_FN(js_uneval_str,             str_uneval,                1,0),
         388                 : #endif
         389                 :     JS_FN(js_decodeURI_str,          str_decodeURI,             1,0),
         390                 :     JS_FN(js_encodeURI_str,          str_encodeURI,             1,0),
         391                 :     JS_FN(js_decodeURIComponent_str, str_decodeURI_Component,   1,0),
         392                 :     JS_FN(js_encodeURIComponent_str, str_encodeURI_Component,   1,0),
         393                 :
         394                 :     JS_FS_END
         395                 : };
         396                 :
         397                 : jschar      js_empty_ucstr[]  = {0};
         398                 : JSSubString js_EmptySubString = {0, js_empty_ucstr};
         399                 :
         400                 : static const unsigned STRING_ELEMENT_ATTRS = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
         401                 :
         402                 : static JSBool
         403             324 : str_enumerate(JSContext *cx, JSObject *obj)
         404                 : {
         405             324 :     JSString *str = obj->asString().unbox();
         406            1692 :     for (size_t i = 0, length = str->length(); i < length; i++) {
         407            1368 :         JSString *str1 = js_NewDependentString(cx, str, i, 1);
         408            1368 :         if (!str1)
         409               0 :             return false;
         410            1368 :         if (!obj->defineElement(cx, i, StringValue(str1),
         411                 :                                 JS_PropertyStub, JS_StrictPropertyStub,
         412            1368 :                                 STRING_ELEMENT_ATTRS)) {
         413               0 :             return false;
         414                 :         }
         415                 :     }
         416                 :
         417             324 :     return true;
         418                 : }
         419                 :
         420                 : static JSBool
         421           99369 : str_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
         422                 :             JSObject **objp)
         423                 : {
         424           99369 :     if (!JSID_IS_INT(id))
         425           93340 :         return JS_TRUE;
         426                 :
         427            6029 :     JSString *str = obj->asString().unbox();
         428                 :
         429            6029 :     int32_t slot = JSID_TO_INT(id);
         430            6029 :     if ((size_t)slot < str->length()) {
         431             189 :         JSString *str1 = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(slot));
         432             189 :         if (!str1)
         433               0 :             return JS_FALSE;
         434             189 :         if (!obj->defineElement(cx, uint32_t(slot), StringValue(str1), NULL, NULL,
         435             189 :                                 STRING_ELEMENT_ATTRS)) {
         436               0 :             return JS_FALSE;
         437                 :         }
         438             189 :         *objp = obj;
         439                 :     }
         440            6029 :     return JS_TRUE;
         441                 : }
         442                 :
         443                 : Class js::StringClass = {
         444                 :     js_String_str,
         445                 :     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
         446                 :     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_String),
         447                 :     JS_PropertyStub,         /* addProperty */
         448                 :     JS_PropertyStub,         /* delProperty */
         449                 :     JS_PropertyStub,         /* getProperty */
         450                 :     JS_StrictPropertyStub,   /* setProperty */
         451                 :     str_enumerate,
         452                 :     (JSResolveOp)str_resolve,
         453                 :     JS_ConvertStub
         454                 : };
         455                 :
         456                 : /*
         457                 :  * Returns a JSString * for the |this| value associated with 'call', or throws
         458                 :  * a TypeError if |this| is null or undefined.  This algorithm is the same as
         459                 :  * calling CheckObjectCoercible(this), then returning ToString(this), as all
         460                 :  * String.prototype.* methods do (other than toString and valueOf).
         461                 :  */
         462                 : static JS_ALWAYS_INLINE JSString *
         463         1233169 : ThisToStringForStringProto(JSContext *cx, CallReceiver call)
         464                 : {
         465         1233169 :     JS_CHECK_RECURSION(cx, return NULL);
         466                 :
         467         1233169 :     if (call.thisv().isString())
         468         1229236 :         return call.thisv().toString();
         469                 :
         470            3933 :     if (call.thisv().isObject()) {
         471            3933 :         JSObject *obj = &call.thisv().toObject();
         472            6678 :         if (obj->isString() &&
         473                 :             ClassMethodIsNative(cx, obj,
         474                 :                                 &StringClass,
         475            2745 :                                 ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
         476            2745 :                                 js_str_toString))
         477                 :         {
         478            2745 :             JSString *str = obj->asString().unbox();
         479            2745 :             call.thisv().setString(str);
         480            2745 :             return str;
         481                 :         }
         482               0 :     } else if (call.thisv().isNullOrUndefined()) {
         483                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
         484               0 :                              call.thisv().isNull() ? "null" : "undefined", "object");
         485               0 :         return NULL;
         486                 :     }
         487                 :
         488            1188 :     JSString *str = ToStringSlow(cx, call.thisv());
         489            1188 :     if (!str)
         490               0 :         return NULL;
         491                 :
         492            1188 :     call.thisv().setString(str);
         493            1188 :     return str;
         494                 : }
         495                 :
         496                 : #if JS_HAS_TOSOURCE
         497                 :
         498                 : /*
         499                 :  * String.prototype.quote is generic (as are most string methods), unlike
         500                 :  * toSource, toString, and valueOf.
         501                 :  */
         502                 : static JSBool
         503               0 : str_quote(JSContext *cx, unsigned argc, Value *vp)
         504                 : {
         505               0 :     CallArgs args = CallArgsFromVp(argc, vp);
         506               0 :     JSString *str = ThisToStringForStringProto(cx, args);
         507               0 :     if (!str)
         508               0 :         return false;
         509               0 :     str = js_QuoteString(cx, str, '"');
         510               0 :     if (!str)
         511               0 :         return false;
         512               0 :     args.rval() = StringValue(str);
         513               0 :     return true;
         514                 : }
         515                 :
         516                 : static JSBool
         517             126 : str_toSource(JSContext *cx, unsigned argc, Value *vp)
         518                 : {
         519             126 :     CallArgs args = CallArgsFromVp(argc, vp);
         520                 :
         521                 :     JSString *str;
         522                 :     bool ok;
         523             126 :     if (!BoxedPrimitiveMethodGuard(cx, args, str_toSource, &str, &ok))
         524              63 :         return ok;
         525                 :
         526              63 :     str = js_QuoteString(cx, str, '"');
         527              63 :     if (!str)
         528               0 :         return false;
         529                 :
         530             126 :     StringBuffer sb(cx);
         531              63 :     if (!sb.append("(new String(") || !sb.append(str) || !sb.append("))"))
         532               0 :         return false;
         533                 :
         534              63 :     str = sb.finishString();
         535              63 :     if (!str)
         536               0 :         return false;
         537              63 :     args.rval() = StringValue(str);
         538              63 :     return true;
         539                 : }
         540                 :
         541                 : #endif /* JS_HAS_TOSOURCE */
         542                 :
         543                 : JSBool
         544             144 : js_str_toString(JSContext *cx, unsigned argc, Value *vp)
         545                 : {
         546             144 :     CallArgs args = CallArgsFromVp(argc, vp);
         547                 :
         548                 :     JSString *str;
         549                 :     bool ok;
         550             144 :     if (!BoxedPrimitiveMethodGuard(cx, args, js_str_toString, &str, &ok))
         551              63 :         return ok;
         552                 :
         553              81 :     args.rval() = StringValue(str);
         554              81 :     return true;
         555                 : }
         556                 :
         557                 : /*
         558                 :  * Java-like string native methods.
         559                 :  */
         560                 :
         561                 : JS_ALWAYS_INLINE bool
         562          509427 : ValueToIntegerRange(JSContext *cx, const Value &v, int32_t *out)
         563                 : {
         564          509427 :     if (v.isInt32()) {
         565          509427 :         *out = v.toInt32();
         566                 :     } else {
         567                 :         double d;
         568               0 :         if (!ToInteger(cx, v, &d))
         569               0 :             return false;
         570               0 :         if (d > INT32_MAX)
         571               0 :             *out = INT32_MAX;
         572               0 :         else if (d < INT32_MIN)
         573               0 :             *out = INT32_MIN;
         574                 :         else
         575               0 :             *out = int32_t(d);
         576                 :     }
         577                 :
         578          509427 :     return true;
         579                 : }
         580                 :
         581                 : static JSBool
         582          254151 : str_substring(JSContext *cx, unsigned argc, Value *vp)
         583                 : {
         584          254151 :     CallArgs args = CallArgsFromVp(argc, vp);
         585                 :
         586          254151 :     JSString *str = ThisToStringForStringProto(cx, args);
         587          254151 :     if (!str)
         588               0 :         return false;
         589                 :
         590                 :     int32_t length, begin, end;
         591          254151 :     if (args.length() > 0) {
         592          254151 :         end = length = int32_t(str->length());
         593                 :
         594          254151 :         if (!ValueToIntegerRange(cx, args[0], &begin))
         595               0 :             return false;
         596                 :
         597          254151 :         if (begin < 0)
         598               0 :             begin = 0;
         599          254151 :         else if (begin > length)
         600              45 :             begin = length;
         601                 :
         602          254151 :         if (args.hasDefined(1)) {
         603          210204 :             if (!ValueToIntegerRange(cx, args[1], &end))
         604               0 :                 return false;
         605                 :
         606          210204 :             if (end > length) {
         607               0 :                 end = length;
         608                 :             } else {
         609          210204 :                 if (end < 0)
         610               0 :                     end = 0;
         611          210204 :                 if (end < begin) {
         612               0 :                     int32_t tmp = begin;
         613               0 :                     begin = end;
         614               0 :                     end = tmp;
         615                 :                 }
         616                 :             }
         617                 :         }
         618                 :
         619          254151 :         str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin));
         620          254151 :         if (!str)
         621               0 :             return false;
         622                 :     }
         623                 :
         624          254151 :     args.rval() = StringValue(str);
         625          254151 :     return true;
         626                 : }
         627                 :
         628                 : JSString* JS_FASTCALL
         629           23400 : js_toLowerCase(JSContext *cx, JSString *str)
         630                 : {
         631           23400 :     size_t n = str->length();
         632           23400 :     const jschar *s = str->getChars(cx);
         633           23400 :     if (!s)
         634               0 :         return NULL;
         635                 :
         636           23400 :     jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
         637           23400 :     if (!news)
         638               0 :         return NULL;
         639          269442 :     for (size_t i = 0; i < n; i++)
         640          246042 :         news[i] = unicode::ToLowerCase(s[i]);
         641           23400 :     news[n] = 0;
         642           23400 :     str = js_NewString(cx, news, n);
         643           23400 :     if (!str) {
         644               0 :         cx->free_(news);
         645               0 :         return NULL;
         646                 :     }
         647           23400 :     return str;
         648                 : }
         649                 :
         650                 : static inline bool
         651           23400 : ToLowerCaseHelper(JSContext *cx, CallReceiver call)
         652                 : {
         653           23400 :     JSString *str = ThisToStringForStringProto(cx, call);
         654           23400 :     if (!str)
         655               0 :         return false;
         656                 :
         657           23400 :     str = js_toLowerCase(cx, str);
         658           23400 :     if (!str)
         659               0 :         return false;
         660                 :
         661           23400 :     call.rval() = StringValue(str);
         662           23400 :     return true;
         663                 : }
         664                 :
         665                 : static JSBool
         666           23400 : str_toLowerCase(JSContext *cx, unsigned argc, Value *vp)
         667                 : {
         668           23400 :     return ToLowerCaseHelper(cx, CallArgsFromVp(argc, vp));
         669                 : }
         670                 :
         671                 : static JSBool
         672               0 : str_toLocaleLowerCase(JSContext *cx, unsigned argc, Value *vp)
         673                 : {
         674               0 :     CallArgs args = CallArgsFromVp(argc, vp);
         675                 :
         676                 :     /*
         677                 :      * Forcefully ignore the first (or any) argument and return toLowerCase(),
         678                 :      * ECMA has reserved that argument, presumably for defining the locale.
         679                 :      */
         680               0 :     if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
         681               0 :         JSString *str = ThisToStringForStringProto(cx, args);
         682               0 :         if (!str)
         683               0 :             return false;
         684                 :
         685                 :         Value result;
         686               0 :         if (!cx->localeCallbacks->localeToLowerCase(cx, str, &result))
         687               0 :             return false;
         688                 :
         689               0 :         args.rval() = result;
         690               0 :         return true;
         691                 :     }
         692                 :
         693               0 :     return ToLowerCaseHelper(cx, args);
         694                 : }
         695                 :
         696                 : JSString* JS_FASTCALL
         697             900 : js_toUpperCase(JSContext *cx, JSString *str)
         698                 : {
         699             900 :     size_t n = str->length();
         700             900 :     const jschar *s = str->getChars(cx);
         701             900 :     if (!s)
         702               0 :         return NULL;
         703             900 :     jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
         704             900 :     if (!news)
         705               0 :         return NULL;
         706            5400 :     for (size_t i = 0; i < n; i++)
         707            4500 :         news[i] = unicode::ToUpperCase(s[i]);
         708             900 :     news[n] = 0;
         709             900 :     str = js_NewString(cx, news, n);
         710             900 :     if (!str) {
         711               0 :         cx->free_(news);
         712               0 :         return NULL;
         713                 :     }
         714             900 :     return str;
         715                 : }
         716                 :
         717                 : static JSBool
         718             900 : ToUpperCaseHelper(JSContext *cx, CallReceiver call)
         719                 : {
         720             900 :     JSString *str = ThisToStringForStringProto(cx, call);
         721             900 :     if (!str)
         722               0 :         return false;
         723                 :
         724             900 :     str = js_toUpperCase(cx, str);
         725             900 :     if (!str)
         726               0 :         return false;
         727                 :
         728             900 :     call.rval() = StringValue(str);
         729             900 :     return true;
         730                 : }
         731                 :
         732                 : static JSBool
         733             900 : str_toUpperCase(JSContext *cx, unsigned argc, Value *vp)
         734                 : {
         735             900 :     return ToUpperCaseHelper(cx, CallArgsFromVp(argc, vp));
         736                 : }
         737                 :
         738                 : static JSBool
         739               0 : str_toLocaleUpperCase(JSContext *cx, unsigned argc, Value *vp)
         740                 : {
         741               0 :     CallArgs args = CallArgsFromVp(argc, vp);
         742                 :
         743                 :     /*
         744                 :      * Forcefully ignore the first (or any) argument and return toUpperCase(),
         745                 :      * ECMA has reserved that argument, presumably for defining the locale.
         746                 :      */
         747               0 :     if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
         748               0 :         JSString *str = ThisToStringForStringProto(cx, args);
         749               0 :         if (!str)
         750               0 :             return false;
         751                 :
         752                 :         Value result;
         753               0 :         if (!cx->localeCallbacks->localeToUpperCase(cx, str, &result))
         754               0 :             return false;
         755                 :
         756               0 :         args.rval() = result;
         757               0 :         return true;
         758                 :     }
         759                 :
         760               0 :     return ToUpperCaseHelper(cx, args);
         761                 : }
         762                 :
         763                 : static JSBool
         764              63 : str_localeCompare(JSContext *cx, unsigned argc, Value *vp)
         765                 : {
         766              63 :     CallArgs args = CallArgsFromVp(argc, vp);
         767              63 :     JSString *str = ThisToStringForStringProto(cx, args);
         768              63 :     if (!str)
         769               0 :         return false;
         770                 :
         771              63 :     if (args.length() == 0) {
         772               9 :         args.rval() = Int32Value(0);
         773                 :     } else {
         774              54 :         JSString *thatStr = ToString(cx, args[0]);
         775              54 :         if (!thatStr)
         776               0 :             return false;
         777                 :
         778              54 :         if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
         779               0 :             args[0].setString(thatStr);
         780                 :
         781                 :             Value result;
         782               0 :             if (!cx->localeCallbacks->localeCompare(cx, str, thatStr, &result))
         783               0 :                 return true;
         784                 :
         785               0 :             args.rval() = result;
         786               0 :             return true;
         787                 :         }
         788                 :
         789                 :         int32_t result;
         790              54 :         if (!CompareStrings(cx, str, thatStr, &result))
         791               0 :             return false;
         792                 :
         793              54 :         args.rval() = Int32Value(result);
         794                 :     }
         795              63 :     return true;
         796                 : }
         797                 :
         798                 : JSBool
         799            5625 : js_str_charAt(JSContext *cx, unsigned argc, Value *vp)
         800                 : {
         801            5625 :     CallArgs args = CallArgsFromVp(argc, vp);
         802                 :
         803                 :     JSString *str;
         804                 :     size_t i;
         805            5625 :     if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
         806            5444 :         str = args.thisv().toString();
         807            5444 :         i = size_t(args[0].toInt32());
         808            5444 :         if (i >= str->length())
         809              54 :             goto out_of_range;
         810                 :     } else {
         811             181 :         str = ThisToStringForStringProto(cx, args);
         812             181 :         if (!str)
         813               0 :             return false;
         814                 :
         815             181 :         double d = 0.0;
         816             181 :         if (args.length() > 0 && !ToInteger(cx, args[0], &d))
         817               0 :             return false;
         818                 :
         819             181 :         if (d < 0 || str->length() <= d)
         820             117 :             goto out_of_range;
         821              64 :         i = size_t(d);
         822                 :     }
         823                 :
         824            5454 :     str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, i);
         825            5454 :     if (!str)
         826               0 :         return false;
         827            5454 :     args.rval() = StringValue(str);
         828            5454 :     return true;
         829                 :
         830                 :   out_of_range:
         831             171 :     args.rval() = StringValue(cx->runtime->emptyString);
         832             171 :     return true;
         833                 : }
         834                 :
         835                 : JSBool
         836          690878 : js_str_charCodeAt(JSContext *cx, unsigned argc, Value *vp)
         837                 : {
         838          690878 :     CallArgs args = CallArgsFromVp(argc, vp);
         839                 :
         840                 :     JSString *str;
         841                 :     size_t i;
         842          690878 :     if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
         843          690423 :         str = args.thisv().toString();
         844          690423 :         i = size_t(args[0].toInt32());
         845          690423 :         if (i >= str->length())
         846             135 :             goto out_of_range;
         847                 :     } else {
         848             455 :         str = ThisToStringForStringProto(cx, args);
         849             455 :         if (!str)
         850               0 :             return false;
         851                 :
         852             455 :         double d = 0.0;
         853             455 :         if (args.length() > 0 && !ToInteger(cx, args[0], &d))
         854               0 :             return false;
         855                 :
         856             455 :         if (d < 0 || str->length() <= d)
         857             306 :             goto out_of_range;
         858             149 :         i = size_t(d);
         859                 :     }
         860                 :
         861                 :     const jschar *chars;
         862          690437 :     chars = str->getChars(cx);
         863          690437 :     if (!chars)
         864               0 :         return false;
         865                 :
         866          690437 :     args.rval() = Int32Value(chars[i]);
         867          690437 :     return true;
         868                 :
         869                 : out_of_range:
         870             441 :     args.rval() = DoubleValue(js_NaN);
         871             441 :     return true;
         872                 : }
         873                 :
         874                 : /*
         875                 :  * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen.
         876                 :  * The patlen argument must be positive and no greater than sBMHPatLenMax.
         877                 :  *
         878                 :  * Return the index of pat in text, or -1 if not found.
         879                 :  */
         880                 : static const uint32_t sBMHCharSetSize = 256; /* ISO-Latin-1 */
         881                 : static const uint32_t sBMHPatLenMax   = 255; /* skip table element is uint8_t */
         882                 : static const int      sBMHBadPattern  = -2;  /* return value if pat is not ISO-Latin-1 */
         883                 :
         884                 : int
         885               0 : js_BoyerMooreHorspool(const jschar *text, uint32_t textlen,
         886                 :                       const jschar *pat, uint32_t patlen)
         887                 : {
         888                 :     uint8_t skip[sBMHCharSetSize];
         889                 :
         890               0 :     JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
         891               0 :     for (uint32_t i = 0; i < sBMHCharSetSize; i++)
         892               0 :         skip[i] = (uint8_t)patlen;
         893               0 :     uint32_t m = patlen - 1;
         894               0 :     for (uint32_t i = 0; i < m; i++) {
         895               0 :         jschar c = pat[i];
         896               0 :         if (c >= sBMHCharSetSize)
         897               0 :             return sBMHBadPattern;
         898               0 :         skip[c] = (uint8_t)(m - i);
         899                 :     }
         900                 :     jschar c;
         901               0 :     for (uint32_t k = m;
         902                 :          k < textlen;
         903               0 :          k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
         904               0 :         for (uint32_t i = k, j = m; ; i--, j--) {
         905               0 :             if (text[i] != pat[j])
         906                 :                 break;
         907               0 :             if (j == 0)
         908               0 :                 return static_cast<int>(i);  /* safe: max string size */
         909                 :         }
         910                 :     }
         911               0 :     return -1;
         912                 : }
         913                 :
         914                 : struct MemCmp {
         915                 :     typedef uint32_t Extent;
         916                 :     static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, uint32_t patlen) {
         917                 :         return (patlen - 1) * sizeof(jschar);
         918                 :     }
         919                 :     static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
         920                 :         return memcmp(p, t, extent) == 0;
         921                 :     }
         922                 : };
         923                 :
         924                 : struct ManualCmp {
         925                 :     typedef const jschar *Extent;
         926            1755 :     static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, uint32_t patlen) {
         927            1755 :         return pat + patlen;
         928                 :     }
         929            1728 :     static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
         930           11169 :         for (; p != extent; ++p, ++t) {
         931            9441 :             if (*p != *t)
         932               0 :                 return false;
         933                 :         }
         934            1728 :         return true;
         935                 :     }
         936                 : };
         937                 :
         938                 : template <class InnerMatch>
         939                 : static int
         940            1755 : UnrolledMatch(const jschar *text, uint32_t textlen, const jschar *pat, uint32_t patlen)
         941                 : {
         942            1755 :     JS_ASSERT(patlen > 0 && textlen > 0);
         943            1755 :     const jschar *textend = text + textlen - (patlen - 1);
         944            1755 :     const jschar p0 = *pat;
         945            1755 :     const jschar *const patNext = pat + 1;
         946            1755 :     const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
         947                 :     uint8_t fixup;
         948                 :
         949            1755 :     const jschar *t = text;
         950            1755 :     switch ((textend - t) & 7) {
         951              72 :       case 0: if (*t++ == p0) { fixup = 8; goto match; }
         952             153 :       case 7: if (*t++ == p0) { fixup = 7; goto match; }
         953             369 :       case 6: if (*t++ == p0) { fixup = 6; goto match; }
         954             252 :       case 5: if (*t++ == p0) { fixup = 5; goto match; }
         955             270 :       case 4: if (*t++ == p0) { fixup = 4; goto match; }
         956             306 :       case 3: if (*t++ == p0) { fixup = 3; goto match; }
         957            1476 :       case 2: if (*t++ == p0) { fixup = 2; goto match; }
         958            1539 :       case 1: if (*t++ == p0) { fixup = 1; goto match; }
         959                 :     }
         960            1098 :     while (t != textend) {
         961             729 :       if (t[0] == p0) { t += 1; fixup = 8; goto match; }
         962             729 :       if (t[1] == p0) { t += 2; fixup = 7; goto match; }
         963             693 :       if (t[2] == p0) { t += 3; fixup = 6; goto match; }
         964             675 :       if (t[3] == p0) { t += 4; fixup = 5; goto match; }
         965             639 :       if (t[4] == p0) { t += 5; fixup = 4; goto match; }
         966             621 :       if (t[5] == p0) { t += 6; fixup = 3; goto match; }
         967             621 :       if (t[6] == p0) { t += 7; fixup = 2; goto match; }
         968             468 :       if (t[7] == p0) { t += 8; fixup = 1; goto match; }
         969             414 :         t += 8;
         970             414 :         continue;
         971               0 :         do {
         972               0 :             if (*t++ == p0) {
         973                 :               match:
         974            1728 :                 if (!InnerMatch::match(patNext, t, extent))
         975               0 :                     goto failed_match;
         976            1728 :                 return t - text - 1;
         977                 :             }
         978                 :           failed_match:;
         979                 :         } while (--fixup > 0);
         980                 :     }
         981              27 :     return -1;
         982                 : }
         983                 :
         984                 : static JS_ALWAYS_INLINE int
         985        18239178 : StringMatch(const jschar *text, uint32_t textlen,
         986                 :             const jschar *pat, uint32_t patlen)
         987                 : {
         988        18239178 :     if (patlen == 0)
         989          200601 :         return 0;
         990        18038577 :     if (textlen < patlen)
         991           18072 :         return -1;
         992                 :
         993                 : #if defined(__i386__) || defined(_M_IX86) || defined(__i386)
         994                 :     /*
         995                 :      * Given enough registers, the unrolled loop below is faster than the
         996                 :      * following loop. 32-bit x86 does not have enough registers.
         997                 :      */
         998        18020505 :     if (patlen == 1) {
         999        18018750 :         const jschar p0 = *pat;
        1000      1144042176 :         for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
        1001      1126565769 :             if (*c == p0)
        1002          542343 :                 return c - text;
        1003                 :         }
        1004        17476407 :         return -1;
        1005                 :     }
        1006                 : #endif
        1007                 :
        1008                 :     /*
        1009                 :      * If the text or pattern string is short, BMH will be more expensive than
        1010                 :      * the basic linear scan due to initialization cost and a more complex loop
        1011                 :      * body. While the correct threshold is input-dependent, we can make a few
        1012                 :      * conservative observations:
        1013                 :      *  - When |textlen| is "big enough", the initialization time will be
        1014                 :      *    proportionally small, so the worst-case slowdown is minimized.
        1015                 :      *  - When |patlen| is "too small", even the best case for BMH will be
        1016                 :      *    slower than a simple scan for large |textlen| due to the more complex
        1017                 :      *    loop body of BMH.
        1018                 :      * From this, the values for "big enough" and "too small" are determined
        1019                 :      * empirically. See bug 526348.
        1020                 :      */
        1021            1755 :     if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
        1022               0 :         int index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
        1023               0 :         if (index != sBMHBadPattern)
        1024               0 :             return index;
        1025                 :     }
        1026                 :
        1027                 :     /*
        1028                 :      * For big patterns with large potential overlap we want the SIMD-optimized
        1029                 :      * speed of memcmp. For small patterns, a simple loop is faster.
        1030                 :      *
        1031                 :      * FIXME: Linux memcmp performance is sad and the manual loop is faster.
        1032                 :      */
        1033                 :     return
        1034                 : #if !defined(__linux__)
        1035                 :            patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
        1036                 :                         :
        1037                 : #endif
        1038            1755 :                           UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
        1039                 : }
        1040                 :
        1041                 : static const size_t sRopeMatchThresholdRatioLog2 = 5;
        1042                 :
        1043                 : /*
        1044                 :  * RopeMatch takes the text to search, the patern to search for in the text.
        1045                 :  * RopeMatch returns false on OOM and otherwise returns the match index through
        1046                 :  * the 'match' outparam (-1 for not found).
        1047                 :  */
        1048                 : static bool
        1049          450018 : RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, uint32_t patlen, int *match)
        1050                 : {
        1051          450018 :     JS_ASSERT(textstr->isRope());
        1052                 :
        1053          450018 :     if (patlen == 0) {
        1054               0 :         *match = 0;
        1055               0 :         return true;
        1056                 :     }
        1057          450018 :     if (textstr->length() < patlen) {
        1058               0 :         *match = -1;
        1059               0 :         return true;
        1060                 :     }
        1061                 :
        1062                 :     /*
        1063                 :      * List of leaf nodes in the rope. If we run out of memory when trying to
        1064                 :      * append to this list, we can still fall back to StringMatch, so use the
        1065                 :      * system allocator so we don't report OOM in that case.
        1066                 :      */
        1067          900036 :     Vector<JSLinearString *, 16, SystemAllocPolicy> strs;
        1068                 :
        1069                 :     /*
        1070                 :      * We don't want to do rope matching if there is a poor node-to-char ratio,
        1071                 :      * since this means spending a lot of time in the match loop below. We also
        1072                 :      * need to build the list of leaf nodes. Do both here: iterate over the
        1073                 :      * nodes so long as there are not too many.
        1074                 :      */
        1075                 :     {
        1076          450018 :         size_t textstrlen = textstr->length();
        1077          450018 :         size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
        1078          900036 :         StringSegmentRange r(cx);
        1079          450018 :         if (!r.init(textstr))
        1080               0 :             return false;
        1081        37192383 :         while (!r.empty()) {
        1082        36309006 :             if (threshold-- == 0 || !strs.append(r.front())) {
        1083           16659 :                 const jschar *chars = textstr->getChars(cx);
        1084           16659 :                 if (!chars)
        1085               0 :                     return false;
        1086           16659 :                 *match = StringMatch(chars, textstrlen, pat, patlen);
        1087           16659 :                 return true;
        1088                 :             }
        1089        36292347 :             if (!r.popFront())
        1090               0 :                 return false;
        1091                 :         }
        1092                 :     }
        1093                 :
        1094                 :     /* Absolute offset from the beginning of the logical string textstr. */
        1095          433359 :     int pos = 0;
        1096                 :
        1097        17877708 :     for (JSLinearString **outerp = strs.begin(); outerp != strs.end(); ++outerp) {
        1098                 :         /* Try to find a match within 'outer'. */
        1099        17877708 :         JSLinearString *outer = *outerp;
        1100        17877708 :         const jschar *chars = outer->chars();
        1101        17877708 :         size_t len = outer->length();
        1102        17877708 :         int matchResult = StringMatch(chars, len, pat, patlen);
        1103        17877708 :         if (matchResult != -1) {
        1104                 :             /* Matched! */
        1105          433359 :             *match = pos + matchResult;
        1106          433359 :             return true;
        1107                 :         }
        1108                 :
        1109                 :         /* Try to find a match starting in 'outer' and running into other nodes. */
        1110        17444349 :         const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1);
        1111        17444349 :         const jschar *const textend = chars + len;
        1112        17444349 :         const jschar p0 = *pat;
        1113        17444349 :         const jschar *const p1 = pat + 1;
        1114        17444349 :         const jschar *const patend = pat + patlen;
        1115        34888887 :         for (const jschar *t = text; t != textend; ) {
        1116             189 :             if (*t++ != p0)
        1117             171 :                 continue;
        1118              18 :             JSLinearString **innerp = outerp;
        1119              18 :             const jschar *ttend = textend;
        1120              36 :             for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) {
        1121              90 :                 while (tt == ttend) {
        1122              18 :                     if (++innerp == strs.end()) {
        1123               0 :                         *match = -1;
        1124               0 :                         return true;
        1125                 :                     }
        1126              18 :                     JSLinearString *inner = *innerp;
        1127              18 :                     tt = inner->chars();
        1128              18 :                     ttend = tt + inner->length();
        1129                 :                 }
        1130              36 :                 if (*pp != *tt)
        1131              18 :                     goto break_continue;
        1132                 :             }
        1133                 :
        1134                 :             /* Matched! */
        1135               0 :             *match = pos + (t - chars) - 1;  /* -1 because of *t++ above */
        1136               0 :             return true;
        1137                 :
        1138                 :           break_continue:;
        1139                 :         }
        1140                 :
        1141        17444349 :         pos += len;
        1142                 :     }
        1143                 :
        1144               0 :     *match = -1;
        1145               0 :     return true;
        1146                 : }
        1147                 :
        1148                 : static JSBool
        1149            1530 : str_indexOf(JSContext *cx, unsigned argc, Value *vp)
        1150                 : {
        1151            1530 :     CallArgs args = CallArgsFromVp(argc, vp);
        1152            1530 :     JSString *str = ThisToStringForStringProto(cx, args);
        1153            1530 :     if (!str)
        1154               0 :         return false;
        1155                 :
        1156            1530 :     JSLinearString *patstr = ArgToRootedString(cx, args, 0);
        1157            1530 :     if (!patstr)
        1158               0 :         return false;
        1159                 :
        1160            1530 :     uint32_t textlen = str->length();
        1161            1530 :     const jschar *text = str->getChars(cx);
        1162            1530 :     if (!text)
        1163               0 :         return false;
        1164                 :
        1165            1530 :     uint32_t patlen = patstr->length();
        1166            1530 :     const jschar *pat = patstr->chars();
        1167                 :
        1168                 :     uint32_t start;
        1169            1530 :     if (args.length() > 1) {
        1170               0 :         if (args[1].isInt32()) {
        1171               0 :             int i = args[1].toInt32();
        1172               0 :             if (i <= 0) {
        1173               0 :                 start = 0;
        1174               0 :             } else if (uint32_t(i) > textlen) {
        1175               0 :                 start = textlen;
        1176               0 :                 textlen = 0;
        1177                 :             } else {
        1178               0 :                 start = i;
        1179               0 :                 text += start;
        1180               0 :                 textlen -= start;
        1181                 :             }
        1182                 :         } else {
        1183                 :             double d;
        1184               0 :             if (!ToInteger(cx, args[1], &d))
        1185               0 :                 return false;
        1186               0 :             if (d <= 0) {
        1187               0 :                 start = 0;
        1188               0 :             } else if (d > textlen) {
        1189               0 :                 start = textlen;
        1190               0 :                 textlen = 0;
        1191                 :             } else {
        1192               0 :                 start = (int)d;
        1193               0 :                 text += start;
        1194               0 :                 textlen -= start;
        1195                 :             }
        1196                 :         }
        1197                 :     } else {
        1198            1530 :         start = 0;
        1199                 :     }
        1200                 :
        1201            1530 :     int match = StringMatch(text, textlen, pat, patlen);
        1202            1530 :     args.rval() = Int32Value((match == -1) ? -1 : start + match);
        1203            1530 :     return true;
        1204                 : }
        1205                 :
        1206                 : static JSBool
        1207               0 : str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
        1208                 : {
        1209               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        1210               0 :     JSString *textstr = ThisToStringForStringProto(cx, args);
        1211               0 :     if (!textstr)
        1212               0 :         return false;
        1213                 :
        1214               0 :     size_t textlen = textstr->length();
        1215               0 :     const jschar *text = textstr->getChars(cx);
        1216               0 :     if (!text)
        1217               0 :         return false;
        1218                 :
        1219               0 :     JSLinearString *patstr = ArgToRootedString(cx, args, 0);
        1220               0 :     if (!patstr)
        1221               0 :         return false;
        1222                 :
        1223               0 :     size_t patlen = patstr->length();
        1224               0 :     const jschar *pat = patstr->chars();
        1225                 :
        1226               0 :     int i = textlen - patlen; // Start searching here
        1227               0 :     if (i < 0) {
        1228               0 :         args.rval() = Int32Value(-1);
        1229               0 :         return true;
        1230                 :     }
        1231                 :
        1232               0 :     if (args.length() > 1) {
        1233               0 :         if (args[1].isInt32()) {
        1234               0 :             int j = args[1].toInt32();
        1235               0 :             if (j <= 0)
        1236               0 :                 i = 0;
        1237               0 :             else if (j < i)
        1238               0 :                 i = j;
        1239                 :         } else {
        1240                 :             double d;
        1241               0 :             if (!ToNumber(cx, args[1], &d))
        1242               0 :                 return false;
        1243               0 :             if (!JSDOUBLE_IS_NaN(d)) {
        1244               0 :                 d = js_DoubleToInteger(d);
        1245               0 :                 if (d <= 0)
        1246               0 :                     i = 0;
        1247               0 :                 else if (d < i)
        1248               0 :                     i = (int)d;
        1249                 :             }
        1250                 :         }
        1251                 :     }
        1252                 :
        1253               0 :     if (patlen == 0) {
        1254               0 :         args.rval() = Int32Value(i);
        1255               0 :         return true;
        1256                 :     }
        1257                 :
        1258               0 :     const jschar *t = text + i;
        1259               0 :     const jschar *textend = text - 1;
        1260               0 :     const jschar p0 = *pat;
        1261               0 :     const jschar *patNext = pat + 1;
        1262               0 :     const jschar *patEnd = pat + patlen;
        1263                 :
        1264               0 :     for (; t != textend; --t) {
        1265               0 :         if (*t == p0) {
        1266               0 :             const jschar *t1 = t + 1;
        1267               0 :             for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
        1268               0 :                 if (*t1 != *p1)
        1269               0 :                     goto break_continue;
        1270                 :             }
        1271               0 :             args.rval() = Int32Value(t - text);
        1272               0 :             return true;
        1273                 :         }
        1274                 :       break_continue:;
        1275                 :     }
        1276                 :
        1277               0 :     args.rval() = Int32Value(-1);
        1278               0 :     return true;
        1279                 : }
        1280                 :
        1281                 : static JSBool
        1282             180 : js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
        1283                 : {
        1284             180 :     CallReceiver call = CallReceiverFromVp(vp);
        1285             180 :     JSString *str = ThisToStringForStringProto(cx, call);
        1286             180 :     if (!str)
        1287               0 :         return false;
        1288             180 :     size_t length = str->length();
        1289             180 :     const jschar *chars = str->getChars(cx);
        1290             180 :     if (!chars)
        1291               0 :         return false;
        1292                 :
        1293             180 :     size_t begin = 0;
        1294             180 :     size_t end = length;
        1295                 :
        1296             180 :     if (trimLeft) {
        1297            3168 :         while (begin < length && unicode::IsSpace(chars[begin]))
        1298            2844 :             ++begin;
        1299                 :     }
        1300                 :
        1301             180 :     if (trimRight) {
        1302             387 :         while (end > begin && unicode::IsSpace(chars[end - 1]))
        1303              27 :             --end;
        1304                 :     }
        1305                 :
        1306             180 :     str = js_NewDependentString(cx, str, begin, end - begin);
        1307             180 :     if (!str)
        1308               0 :         return false;
        1309                 :
        1310             180 :     call.rval() = StringValue(str);
        1311             180 :     return true;
        1312                 : }
        1313                 :
        1314                 : static JSBool
        1315             162 : str_trim(JSContext *cx, unsigned argc, Value *vp)
        1316                 : {
        1317             162 :     return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
        1318                 : }
        1319                 :
        1320                 : static JSBool
        1321               0 : str_trimLeft(JSContext *cx, unsigned argc, Value *vp)
        1322                 : {
        1323               0 :     return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
        1324                 : }
        1325                 :
        1326                 : static JSBool
        1327              18 : str_trimRight(JSContext *cx, unsigned argc, Value *vp)
        1328                 : {
        1329              18 :     return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
        1330                 : }
        1331                 :
        1332                 : /*
        1333                 :  * Perl-inspired string functions.
        1334                 :  */
        1335                 :
        1336                 : /* Result of a successfully performed flat match. */
        1337                 : class FlatMatch
        1338                 : {
        1339                 :     JSAtom       *patstr;
        1340                 :     const jschar *pat;
        1341                 :     size_t       patlen;
        1342                 :     int32_t      match_;
        1343                 :
        1344                 :     friend class StringRegExpGuard;
        1345                 :
        1346                 :   public:
        1347          907957 :     FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
        1348            3960 :     JSLinearString *pattern() const { return patstr; }
        1349           10332 :     size_t patternLength() const { return patlen; }
        1350                 :
        1351                 :     /*
        1352                 :      * Note: The match is -1 when the match is performed successfully,
        1353                 :      * but no match is found.
        1354                 :      */
        1355          522261 :     int32_t match() const { return match_; }
        1356                 : };
        1357                 :
        1358                 : static inline bool
        1359          820998 : IsRegExpMetaChar(jschar c)
        1360                 : {
        1361          820998 :     switch (c) {
        1362                 :       /* Taken from the PatternCharacter production in 15.10.1. */
        1363                 :       case '^': case '$': case '\\': case '.': case '*': case '+':
        1364                 :       case '?': case '(': case ')': case '[': case ']': case '{':
        1365                 :       case '}': case '|':
        1366           20349 :         return true;
        1367                 :       default:
        1368          800649 :         return false;
        1369                 :     }
        1370                 : }
        1371                 :
        1372                 : static inline bool
        1373          510939 : HasRegExpMetaChars(const jschar *chars, size_t length)
        1374                 : {
        1375         1310589 :     for (size_t i = 0; i < length; ++i) {
        1376          819495 :         if (IsRegExpMetaChar(chars[i]))
        1377           19845 :             return true;
        1378                 :     }
        1379          491094 :     return false;
        1380                 : }
        1381                 :
        1382                 : /*
        1383                 :  * StringRegExpGuard factors logic out of String regexp operations.
        1384                 :  *
        1385                 :  * |optarg| indicates in which argument position RegExp flags will be found, if
        1386                 :  * present. This is a Mozilla extension and not part of any ECMA spec.
        1387                 :  */
        1388                 : class StringRegExpGuard
        1389          907957 : {
        1390                 :     StringRegExpGuard(const StringRegExpGuard &) MOZ_DELETE;
        1391                 :     void operator=(const StringRegExpGuard &) MOZ_DELETE;
        1392                 :
        1393                 :     RegExpGuard re_;
        1394                 :     FlatMatch   fm;
        1395                 :
        1396                 :     /*
        1397                 :      * Upper bound on the number of characters we are willing to potentially
        1398                 :      * waste on searching for RegExp meta-characters.
        1399                 :      */
        1400                 :     static const size_t MAX_FLAT_PAT_LEN = 256;
        1401                 :
        1402                 :     static JSAtom *
        1403             423 :     flattenPattern(JSContext *cx, JSAtom *patstr)
        1404                 :     {
        1405             846 :         StringBuffer sb(cx);
        1406             423 :         if (!sb.reserve(patstr->length()))
        1407               0 :             return NULL;
        1408                 :
        1409                 :         static const jschar ESCAPE_CHAR = '\\';
        1410             423 :         const jschar *chars = patstr->chars();
        1411             423 :         size_t len = patstr->length();
        1412            1926 :         for (const jschar *it = chars; it != chars + len; ++it) {
        1413            1503 :             if (IsRegExpMetaChar(*it)) {
        1414             504 :                 if (!sb.append(ESCAPE_CHAR) || !sb.append(*it))
        1415               0 :                     return NULL;
        1416                 :             } else {
        1417             999 :                 if (!sb.append(*it))
        1418               0 :                     return NULL;
        1419                 :             }
        1420                 :         }
        1421             423 :         return sb.finishAtom();
        1422                 :     }
        1423                 :
        1424                 :   public:
        1425          907957 :     StringRegExpGuard() {}
        1426                 :
        1427                 :     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
        1428          907957 :     bool init(JSContext *cx, CallArgs args, bool convertVoid = false)
        1429                 :     {
        1430          907957 :         if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
        1431          349750 :             if (!RegExpToShared(cx, args[0].toObject(), &re_))
        1432               0 :                 return false;
        1433                 :         } else {
        1434          558207 :             if (convertVoid && !args.hasDefined(0)) {
        1435           11556 :                 fm.patstr = cx->runtime->emptyString;
        1436           11556 :                 return true;
        1437                 :             }
        1438                 :
        1439          546651 :             JSString *arg = ArgToRootedString(cx, args, 0);
        1440          546651 :             if (!arg)
        1441              54 :                 return false;
        1442                 :
        1443          546597 :             fm.patstr = js_AtomizeString(cx, arg);
        1444          546597 :             if (!fm.patstr)
        1445               0 :                 return false;
        1446                 :         }
        1447          896347 :         return true;
        1448                 :     }
        1449                 :
        1450                 :     /*
        1451                 :      * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
        1452                 :      * pattern string, or a lengthy pattern string can thwart this process.
        1453                 :      *
        1454                 :      * |checkMetaChars| looks for regexp metachars in the pattern string.
        1455                 :      *
        1456                 :      * Return whether flat matching could be used.
        1457                 :      *
        1458                 :      * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
        1459                 :      */
        1460                 :     const FlatMatch *
        1461          907903 :     tryFlatMatch(JSContext *cx, JSString *textstr, unsigned optarg, unsigned argc,
        1462                 :                  bool checkMetaChars = true)
        1463                 :     {
        1464          907903 :         if (re_.initialized())
        1465          349750 :             return NULL;
        1466                 :
        1467          558153 :         fm.pat = fm.patstr->chars();
        1468          558153 :         fm.patlen = fm.patstr->length();
        1469                 :
        1470          558153 :         if (optarg < argc)
        1471           23463 :             return NULL;
        1472                 :
        1473         1045629 :         if (checkMetaChars &&
        1474          510939 :             (fm.patlen > MAX_FLAT_PAT_LEN || HasRegExpMetaChars(fm.pat, fm.patlen))) {
        1475           19845 :             return NULL;
        1476                 :         }
        1477                 :
        1478                 :         /*
        1479                 :          * textstr could be a rope, so we want to avoid flattening it for as
        1480                 :          * long as possible.
        1481                 :          */
        1482          514845 :         if (textstr->isRope()) {
        1483          450018 :             if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_))
        1484               0 :                 return NULL;
        1485                 :         } else {
        1486           64827 :             const jschar *text = textstr->asLinear().chars();
        1487           64827 :             size_t textlen = textstr->length();
        1488           64827 :             fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
        1489                 :         }
        1490          514845 :         return &fm;
        1491                 :     }
        1492                 :
        1493                 :     /* If the pattern is not already a regular expression, make it so. */
        1494          393058 :     bool normalizeRegExp(JSContext *cx, bool flat, unsigned optarg, CallArgs args)
        1495                 :     {
        1496          393058 :         if (re_.initialized())
        1497          349750 :             return true;
        1498                 :
        1499                 :         /* Build RegExp from pattern string. */
        1500                 :         JSString *opt;
        1501           43308 :         if (optarg < args.length()) {
        1502           23463 :             opt = ToString(cx, args[optarg]);
        1503           23463 :             if (!opt)
        1504               0 :                 return false;
        1505                 :         } else {
        1506           19845 :             opt = NULL;
        1507                 :         }
        1508                 :
        1509                 :         JSAtom *patstr;
        1510           43308 :         if (flat) {
        1511             423 :             patstr = flattenPattern(cx, fm.patstr);
        1512             423 :             if (!patstr)
        1513               0 :                 return false;
        1514                 :         } else {
        1515           42885 :             patstr = fm.patstr;
        1516                 :         }
        1517           43308 :         JS_ASSERT(patstr);
        1518                 :
        1519           43308 :         return cx->compartment->regExps.get(cx, patstr, opt, &re_);
        1520                 :     }
        1521                 :
        1522          429158 :     RegExpShared &regExp() { return *re_; }
        1523                 : };
        1524                 :
        1525                 : /* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
        1526                 : static JS_ALWAYS_INLINE bool
        1527         2018898 : Matched(RegExpExecType type, const Value &v)
        1528                 : {
        1529         2018898 :     return (type == RegExpTest) ? v.isTrue() : !v.isNull();
        1530                 : }
        1531                 :
        1532                 : typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
        1533                 :
        1534                 : /*
        1535                 :  * BitOR-ing these flags allows the DoMatch caller to control when how the
        1536                 :  * RegExp engine is called and when callbacks are fired.
        1537                 :  */
        1538                 : enum MatchControlFlags {
        1539                 :    TEST_GLOBAL_BIT         = 0x1, /* use RegExp.test for global regexps */
        1540                 :    TEST_SINGLE_BIT         = 0x2, /* use RegExp.test for non-global regexps */
        1541                 :    CALLBACK_ON_SINGLE_BIT  = 0x4, /* fire callback on non-global match */
        1542                 :
        1543                 :    MATCH_ARGS    = TEST_GLOBAL_BIT,
        1544                 :    MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
        1545                 :    REPLACE_ARGS  = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
        1546                 : };
        1547                 :
        1548                 : /* Factor out looping and matching logic. */
        1549                 : static bool
        1550          377857 : DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re,
        1551                 :         DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
        1552                 : {
        1553          377857 :     JSLinearString *linearStr = str->ensureLinear(cx);
        1554          377857 :     if (!linearStr)
        1555               0 :         return false;
        1556                 :
        1557          377857 :     const jschar *chars = linearStr->chars();
        1558          377857 :     size_t length = linearStr->length();
        1559                 :
        1560          377857 :     if (re.global()) {
        1561          265527 :         RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
        1562         1971702 :         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
        1563         1932804 :             if (!JS_CHECK_OPERATION_LIMIT(cx))
        1564               0 :                 return false;
        1565         1932804 :             if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
        1566               0 :                 return false;
        1567         1932804 :             if (!Matched(type, *rval))
        1568          226629 :                 break;
        1569         1706175 :             if (!callback(cx, res, count, data))
        1570               0 :                 return false;
        1571         1706175 :             if (!res->matched())
        1572           77850 :                 ++i;
        1573                 :         }
        1574                 :     } else {
        1575          112330 :         RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
        1576          112330 :         bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
        1577          112330 :         size_t i = 0;
        1578          112330 :         if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
        1579               0 :             return false;
        1580          112330 :         if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
        1581               9 :             return false;
        1582                 :     }
        1583          377848 :     return true;
        1584                 : }
        1585                 :
        1586                 : static bool
        1587           25920 : BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, CallArgs *args)
        1588                 : {
        1589           25920 :     if (fm.match() < 0) {
        1590           21960 :         args->rval() = NullValue();
        1591           21960 :         return true;
        1592                 :     }
        1593                 :
        1594                 :     /* For this non-global match, produce a RegExp.exec-style array. */
        1595            3960 :     JSObject *obj = NewSlowEmptyArray(cx);
        1596            3960 :     if (!obj)
        1597               0 :         return false;
        1598                 :
        1599           11880 :     if (!obj->defineElement(cx, 0, StringValue(fm.pattern())) ||
        1600            3960 :         !obj->defineProperty(cx, cx->runtime->atomState.indexAtom, Int32Value(fm.match())) ||
        1601            3960 :         !obj->defineProperty(cx, cx->runtime->atomState.inputAtom, StringValue(textstr)))
        1602                 :     {
        1603               0 :         return false;
        1604                 :     }
        1605                 :
        1606            3960 :     args->rval() = ObjectValue(*obj);
        1607            3960 :     return true;
        1608                 : }
        1609                 :
        1610                 : typedef JSObject **MatchArgType;
        1611                 :
        1612                 : /*
        1613                 :  * DoMatch will only callback on global matches, hence this function builds
        1614                 :  * only the "array of matches" returned by match on global regexps.
        1615                 :  */
        1616                 : static bool
        1617            2187 : MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
        1618                 : {
        1619            2187 :     JS_ASSERT(count <= JSID_INT_MAX);  /* by max string length */
        1620                 :
        1621            2187 :     JSObject *&arrayobj = *static_cast<MatchArgType>(p);
        1622            2187 :     if (!arrayobj) {
        1623             405 :         arrayobj = NewDenseEmptyArray(cx);
        1624             405 :         if (!arrayobj)
        1625               0 :             return false;
        1626                 :     }
        1627                 :
        1628                 :     Value v;
        1629            2187 :     return res->createLastMatch(cx, &v) && arrayobj->defineElement(cx, count, v);
        1630                 : }
        1631                 :
        1632                 : JSBool
        1633           62038 : js::str_match(JSContext *cx, unsigned argc, Value *vp)
        1634                 : {
        1635           62038 :     CallArgs args = CallArgsFromVp(argc, vp);
        1636           62038 :     JSString *str = ThisToStringForStringProto(cx, args);
        1637           62038 :     if (!str)
        1638               0 :         return false;
        1639                 :
        1640          124076 :     StringRegExpGuard g;
        1641           62038 :     if (!g.init(cx, args, true))
        1642              18 :         return false;
        1643                 :
        1644           62020 :     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length()))
        1645           25920 :         return BuildFlatMatchArray(cx, str, *fm, &args);
        1646                 :
        1647                 :     /* Return if there was an error in tryFlatMatch. */
        1648           36100 :     if (cx->isExceptionPending())
        1649               0 :         return false;
        1650                 :
        1651           36100 :     if (!g.normalizeRegExp(cx, false, 1, args))
        1652               0 :         return false;
        1653                 :
        1654           36100 :     JSObject *array = NULL;
        1655           36100 :     MatchArgType arg = &array;
        1656           36100 :     RegExpStatics *res = cx->regExpStatics();
        1657                 :     Value rval;
        1658           36100 :     if (!DoMatch(cx, res, str, g.regExp(), MatchCallback, arg, MATCH_ARGS, &rval))
        1659               0 :         return false;
        1660                 :
        1661           36100 :     if (g.regExp().global())
        1662            9864 :         args.rval() = ObjectOrNullValue(array);
        1663                 :     else
        1664           26236 :         args.rval() = rval;
        1665           36100 :     return true;
        1666                 : }
        1667                 :
        1668                 : JSBool
        1669          480393 : js::str_search(JSContext *cx, unsigned argc, Value *vp)
        1670                 : {
        1671          480393 :     CallArgs args = CallArgsFromVp(argc, vp);
        1672          480393 :     JSString *str = ThisToStringForStringProto(cx, args);
        1673          480393 :     if (!str)
        1674               0 :         return false;
        1675                 :
        1676          960786 :     StringRegExpGuard g;
        1677          480393 :     if (!g.init(cx, args, true))
        1678              18 :         return false;
        1679          480375 :     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) {
        1680          465174 :         args.rval() = Int32Value(fm->match());
        1681          465174 :         return true;
        1682                 :     }
        1683                 :
        1684           15201 :     if (cx->isExceptionPending())  /* from tryFlatMatch */
        1685               0 :         return false;
        1686                 :
        1687           15201 :     if (!g.normalizeRegExp(cx, false, 1, args))
        1688               0 :         return false;
        1689                 :
        1690           15201 :     JSLinearString *linearStr = str->ensureLinear(cx);
        1691           15201 :     if (!linearStr)
        1692               0 :         return false;
        1693                 :
        1694           15201 :     const jschar *chars = linearStr->chars();
        1695           15201 :     size_t length = linearStr->length();
        1696           15201 :     RegExpStatics *res = cx->regExpStatics();
        1697                 :
        1698                 :     /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
        1699           15201 :     size_t i = 0;
        1700                 :     Value result;
        1701           15201 :     if (!ExecuteRegExp(cx, res, g.regExp(), linearStr, chars, length, &i, RegExpTest, &result))
        1702               0 :         return false;
        1703                 :
        1704           15201 :     if (result.isTrue())
        1705              63 :         args.rval() = Int32Value(res->matchStart());
        1706                 :     else
        1707           15138 :         args.rval() = Int32Value(-1);
        1708           15201 :     return true;
        1709                 : }
        1710                 :
        1711                 : struct ReplaceData
        1712          365526 : {
        1713          365526 :     ReplaceData(JSContext *cx)
        1714          365526 :      : sb(cx)
        1715          365526 :     {}
        1716                 :
        1717                 :     JSString           *str;           /* 'this' parameter object as a string */
        1718                 :     StringRegExpGuard  g;              /* regexp parameter object and private data */
        1719                 :     JSObject           *lambda;        /* replacement function object or null */
        1720                 :     JSObject           *elembase;      /* object for function(a){return b[a]} replace */
        1721                 :     JSLinearString     *repstr;        /* replacement string */
        1722                 :     const jschar       *dollar;        /* null or pointer to first $ in repstr */
        1723                 :     const jschar       *dollarEnd;     /* limit pointer for js_strchr_limit */
        1724                 :     int                leftIndex;      /* left context index in str->chars */
        1725                 :     JSSubString        dollarStr;      /* for "$$" InterpretDollar result */
        1726                 :     bool               calledBack;     /* record whether callback has been called */
        1727                 :     InvokeArgsGuard    args;           /* arguments for lambda call */
        1728                 :     StringBuffer       sb;             /* buffer built during DoMatch */
        1729                 : };
        1730                 :
        1731                 : static bool
        1732            1476 : InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep,
        1733                 :                 ReplaceData &rdata, JSSubString *out, size_t *skip)
        1734                 : {
        1735            1476 :     JS_ASSERT(*dp == '$');
        1736                 :
        1737                 :     /* If there is only a dollar, bail now */
        1738            1476 :     if (dp + 1 >= ep)
        1739               0 :         return false;
        1740                 :
        1741                 :     /* Interpret all Perl match-induced dollar variables. */
        1742            1476 :     jschar dc = dp[1];
        1743            1476 :     if (JS7_ISDEC(dc)) {
        1744                 :         /* ECMA-262 Edition 3: 1-9 or 01-99 */
        1745            1458 :         unsigned num = JS7_UNDEC(dc);
        1746            1458 :         if (num > res->parenCount())
        1747               0 :             return false;
        1748                 :
        1749            1458 :         const jschar *cp = dp + 2;
        1750            1458 :         if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
        1751               0 :             unsigned tmp = 10 * num + JS7_UNDEC(dc);
        1752               0 :             if (tmp <= res->parenCount()) {
        1753               0 :                 cp++;
        1754               0 :                 num = tmp;
        1755                 :             }
        1756                 :         }
        1757            1458 :         if (num == 0)
        1758               0 :             return false;
        1759                 :
        1760            1458 :         *skip = cp - dp;
        1761                 :
        1762            1458 :         JS_ASSERT(num <= res->parenCount());
        1763                 :
        1764                 :         /*
        1765                 :          * Note: we index to get the paren with the (1-indexed) pair
        1766                 :          * number, as opposed to a (0-indexed) paren number.
        1767                 :          */
        1768            1458 :         res->getParen(num, out);
        1769            1458 :         return true;
        1770                 :     }
        1771                 :
        1772              18 :     *skip = 2;
        1773              18 :     switch (dc) {
        1774                 :       case '$':
        1775               0 :         rdata.dollarStr.chars = dp;
        1776               0 :         rdata.dollarStr.length = 1;
        1777               0 :         *out = rdata.dollarStr;
        1778               0 :         return true;
        1779                 :       case '&':
        1780              18 :         res->getLastMatch(out);
        1781              18 :         return true;
        1782                 :       case '+':
        1783               0 :         res->getLastParen(out);
        1784               0 :         return true;
        1785                 :       case '`':
        1786               0 :         res->getLeftContext(out);
        1787               0 :         return true;
        1788                 :       case '\'':
        1789               0 :         res->getRightContext(out);
        1790               0 :         return true;
        1791                 :     }
        1792               0 :     return false;
        1793                 : }
        1794                 :
        1795                 : static bool
        1796         1765404 : FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
        1797                 : {
        1798         1765404 :     if (JSObject *base = rdata.elembase) {
        1799                 :         /*
        1800                 :          * The base object is used when replace was passed a lambda which looks like
        1801                 :          * 'function(a) { return b[a]; }' for the base object b.  b will not change
        1802                 :          * in the course of the replace unless we end up making a scripted call due
        1803                 :          * to accessing a scripted getter or a value with a scripted toString.
        1804                 :          */
        1805          733383 :         JS_ASSERT(rdata.lambda);
        1806          733383 :         JS_ASSERT(!base->getOps()->lookupProperty);
        1807          733383 :         JS_ASSERT(!base->getOps()->getProperty);
        1808                 :
        1809                 :         Value match;
        1810          733383 :         if (!res->createLastMatch(cx, &match))
        1811               0 :             return false;
        1812          733383 :         JSString *str = match.toString();
        1813                 :
        1814                 :         JSAtom *atom;
        1815          733383 :         if (str->isAtom()) {
        1816          733383 :             atom = &str->asAtom();
        1817                 :         } else {
        1818               0 :             atom = js_AtomizeString(cx, str);
        1819               0 :             if (!atom)
        1820               0 :                 return false;
        1821                 :         }
        1822                 :
        1823                 :         Value v;
        1824          733383 :         if (HasDataProperty(cx, base, atom, &v) && v.isString()) {
        1825          733347 :             rdata.repstr = v.toString()->ensureLinear(cx);
        1826          733347 :             if (!rdata.repstr)
        1827               0 :                 return false;
        1828          733347 :             *sizep = rdata.repstr->length();
        1829          733347 :             return true;
        1830                 :         }
        1831                 :
        1832                 :         /*
        1833                 :          * Couldn't handle this property, fall through and despecialize to the
        1834                 :          * general lambda case.
        1835                 :          */
        1836              36 :         rdata.elembase = NULL;
        1837                 :     }
        1838                 :
        1839         1032057 :     if (JSObject *lambda = rdata.lambda) {
        1840           55818 :         PreserveRegExpStatics staticsGuard(res);
        1841           27909 :         if (!staticsGuard.init(cx))
        1842               0 :             return false;
        1843                 :
        1844                 :         /*
        1845                 :          * In the lambda case, not only do we find the replacement string's
        1846                 :          * length, we compute repstr and return it via rdata for use within
        1847                 :          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
        1848                 :          * index, input), i.e., all the properties of a regexp match array.
        1849                 :          * For $&, etc., we must create string jsvals from cx->regExpStatics.
        1850                 :          * We grab up stack space to keep the newborn strings GC-rooted.
        1851                 :          */
        1852           27909 :         unsigned p = res->parenCount();
        1853           27909 :         unsigned argc = 1 + p + 2;
        1854                 :
        1855           27909 :         InvokeArgsGuard &args = rdata.args;
        1856           27909 :         if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args))
        1857               0 :             return false;
        1858                 :
        1859           27909 :         args.setCallee(ObjectValue(*lambda));
        1860           27909 :         args.thisv() = UndefinedValue();
        1861                 :
        1862                 :         /* Push $&, $1, $2, ... */
        1863           27909 :         unsigned argi = 0;
        1864           27909 :         if (!res->createLastMatch(cx, &args[argi++]))
        1865               0 :             return false;
        1866                 :
        1867           50427 :         for (size_t i = 0; i < res->parenCount(); ++i) {
        1868           22518 :             if (!res->createParen(cx, i + 1, &args[argi++]))
        1869               0 :                 return false;
        1870                 :         }
        1871                 :
        1872                 :         /* Push match index and input string. */
        1873           27909 :         args[argi++].setInt32(res->matchStart());
        1874           27909 :         args[argi].setString(rdata.str);
        1875                 :
        1876           27909 :         if (!Invoke(cx, args))
        1877               9 :             return false;
        1878                 :
        1879                 :         /* root repstr: rdata is on the stack, so scanned by conservative gc. */
        1880           27900 :         JSString *repstr = ToString(cx, args.rval());
        1881           27900 :         if (!repstr)
        1882               0 :             return false;
        1883           27900 :         rdata.repstr = repstr->ensureLinear(cx);
        1884           27900 :         if (!rdata.repstr)
        1885               0 :             return false;
        1886           27900 :         *sizep = rdata.repstr->length();
        1887           27900 :         return true;
        1888                 :     }
        1889                 :
        1890         1004148 :     JSString *repstr = rdata.repstr;
        1891         1004148 :     size_t replen = repstr->length();
        1892         1004886 :     for (const jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp;
        1893                 :          dp = js_strchr_limit(dp, '$', ep)) {
        1894                 :         JSSubString sub;
        1895                 :         size_t skip;
        1896             738 :         if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
        1897             738 :             replen += sub.length - skip;
        1898             738 :             dp += skip;
        1899                 :         } else {
        1900               0 :             dp++;
        1901                 :         }
        1902                 :     }
        1903         1004148 :     *sizep = replen;
        1904         1004148 :     return true;
        1905                 : }
        1906                 :
        1907                 : /*
        1908                 :  * Precondition: |rdata.sb| already has necessary growth space reserved (as
        1909                 :  * derived from FindReplaceLength).
        1910                 :  */
        1911                 : static void
        1912         1765395 : DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
        1913                 : {
        1914         1765395 :     JSLinearString *repstr = rdata.repstr;
        1915                 :     const jschar *cp;
        1916         1765395 :     const jschar *bp = cp = repstr->chars();
        1917                 :
        1918         1765395 :     const jschar *dp = rdata.dollar;
        1919         1765395 :     const jschar *ep = rdata.dollarEnd;
        1920         1766133 :     for (; dp; dp = js_strchr_limit(dp, '$', ep)) {
        1921                 :         /* Move one of the constant portions of the replacement value. */
        1922             738 :         size_t len = dp - cp;
        1923             738 :         rdata.sb.infallibleAppend(cp, len);
        1924             738 :         cp = dp;
        1925                 :
        1926                 :         JSSubString sub;
        1927                 :         size_t skip;
        1928             738 :         if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
        1929             738 :             len = sub.length;
        1930             738 :             rdata.sb.infallibleAppend(sub.chars, len);
        1931             738 :             cp += skip;
        1932             738 :             dp += skip;
        1933                 :         } else {
        1934               0 :             dp++;
        1935                 :         }
        1936                 :     }
        1937         1765395 :     rdata.sb.infallibleAppend(cp, repstr->length() - (cp - bp));
        1938         1765395 : }
        1939                 :
        1940                 : static bool
        1941         1765404 : ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
        1942                 : {
        1943         1765404 :     ReplaceData &rdata = *static_cast<ReplaceData *>(p);
        1944                 :
        1945         1765404 :     rdata.calledBack = true;
        1946         1765404 :     JSLinearString &str = rdata.str->asLinear();  /* flattened for regexp */
        1947         1765404 :     size_t leftoff = rdata.leftIndex;
        1948         1765404 :     const jschar *left = str.chars() + leftoff;
        1949         1765404 :     size_t leftlen = res->matchStart() - leftoff;
        1950         1765404 :     rdata.leftIndex = res->matchLimit();
        1951                 :
        1952         1765404 :     size_t replen = 0;  /* silence 'unused' warning */
        1953         1765404 :     if (!FindReplaceLength(cx, res, rdata, &replen))
        1954               9 :         return false;
        1955                 :
        1956         1765395 :     size_t growth = leftlen + replen;
        1957         1765395 :     if (!rdata.sb.reserve(rdata.sb.length() + growth))
        1958               0 :         return false;
        1959         1765395 :     rdata.sb.infallibleAppend(left, leftlen); /* skipped-over portion of the search value */
        1960         1765395 :     DoReplace(cx, res, rdata);
        1961         1765395 :     return true;
        1962                 : }
        1963                 :
        1964                 : static bool
        1965            3438 : BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
        1966                 :                      const FlatMatch &fm, CallArgs *args)
        1967                 : {
        1968            3438 :     RopeBuilder builder(cx);
        1969            3438 :     size_t match = fm.match();
        1970            3438 :     size_t matchEnd = match + fm.patternLength();
        1971                 :
        1972            3438 :     if (textstr->isRope()) {
        1973                 :         /*
        1974                 :          * If we are replacing over a rope, avoid flattening it by iterating
        1975                 :          * through it, building a new rope.
        1976                 :          */
        1977              18 :         StringSegmentRange r(cx);
        1978               9 :         if (!r.init(textstr))
        1979               0 :             return false;
        1980               9 :         size_t pos = 0;
        1981              36 :         while (!r.empty()) {
        1982              18 :             JSString *str = r.front();
        1983              18 :             size_t len = str->length();
        1984              18 :             size_t strEnd = pos + len;
        1985              18 :             if (pos < matchEnd && strEnd > match) {
        1986                 :                 /*
        1987                 :                  * We need to special-case any part of the rope that overlaps
        1988                 :                  * with the replacement string.
        1989                 :                  */
        1990               9 :                 if (match >= pos) {
        1991                 :                     /*
        1992                 :                      * If this part of the rope overlaps with the left side of
        1993                 :                      * the pattern, then it must be the only one to overlap with
        1994                 :                      * the first character in the pattern, so we include the
        1995                 :                      * replacement string here.
        1996                 :                      */
        1997               9 :                     JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos);
        1998              27 :                     if (!leftSide ||
        1999               9 :                         !builder.append(leftSide) ||
        2000               9 :                         !builder.append(repstr)) {
        2001               0 :                         return false;
        2002                 :                     }
        2003                 :                 }
        2004                 :
        2005                 :                 /*
        2006                 :                  * If str runs off the end of the matched string, append the
        2007                 :                  * last part of str.
        2008                 :                  */
        2009               9 :                 if (strEnd > matchEnd) {
        2010                 :                     JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos,
        2011               0 :                                                                 strEnd - matchEnd);
        2012               0 :                     if (!rightSide || !builder.append(rightSide))
        2013               0 :                         return false;
        2014               9 :                 }
        2015                 :             } else {
        2016               9 :                 if (!builder.append(str))
        2017               0 :                     return false;
        2018                 :             }
        2019              18 :             pos += str->length();
        2020              18 :             if (!r.popFront())
        2021               0 :                 return false;
        2022                 :         }
        2023                 :     } else {
        2024            3429 :         JSString *leftSide = js_NewDependentString(cx, textstr, 0, match);
        2025            3429 :         if (!leftSide)
        2026               0 :             return false;
        2027            3429 :         JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(),
        2028            6858 :                                                     textstr->length() - match - fm.patternLength());
        2029           13716 :         if (!rightSide ||
        2030            3429 :             !builder.append(leftSide) ||
        2031            3429 :             !builder.append(repstr) ||
        2032            3429 :             !builder.append(rightSide)) {
        2033               0 :             return false;
        2034                 :         }
        2035                 :     }
        2036                 :
        2037            3438 :     args->rval() = StringValue(builder.result());
        2038            3438 :     return true;
        2039                 : }
        2040                 :
        2041                 : /*
        2042                 :  * Perform a linear-scan dollar substitution on the replacement text,
        2043                 :  * constructing a result string that looks like:
        2044                 :  *
        2045                 :  *      newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
        2046                 :  */
        2047                 : static inline bool
        2048              18 : BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr,
        2049                 :                        const jschar *firstDollar, const FlatMatch &fm, CallArgs *args)
        2050                 : {
        2051              18 :     JSLinearString *textstr = textstrArg->ensureLinear(cx);
        2052              18 :     if (!textstr)
        2053               0 :         return NULL;
        2054                 :
        2055              18 :     JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
        2056              18 :     size_t matchStart = fm.match();
        2057              18 :     size_t matchLimit = matchStart + fm.patternLength();
        2058                 :
        2059                 :     /*
        2060                 :      * Most probably:
        2061                 :      *
        2062                 :      *      len(newstr) >= len(orig) - len(match) + len(replacement)
        2063                 :      *
        2064                 :      * Note that dollar vars _could_ make the resulting text smaller than this.
        2065                 :      */
        2066              36 :     StringBuffer newReplaceChars(cx);
        2067              18 :     if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
        2068               0 :         return false;
        2069                 :
        2070                 :     /* Move the pre-dollar chunk in bulk. */
        2071              18 :     newReplaceChars.infallibleAppend(repstr->chars(), firstDollar);
        2072                 :
        2073                 :     /* Move the rest char-by-char, interpreting dollars as we encounter them. */
        2074                 : #define ENSURE(__cond) if (!(__cond)) return false;
        2075              18 :     const jschar *repstrLimit = repstr->chars() + repstr->length();
        2076              63 :     for (const jschar *it = firstDollar; it < repstrLimit; ++it) {
        2077              45 :         if (*it != '$' || it == repstrLimit - 1) {
        2078              27 :             ENSURE(newReplaceChars.append(*it));
        2079              27 :             continue;
        2080                 :         }
        2081                 :
        2082              18 :         switch (*(it + 1)) {
        2083                 :           case '$': /* Eat one of the dollars. */
        2084               0 :             ENSURE(newReplaceChars.append(*it));
        2085               0 :             break;
        2086                 :           case '&':
        2087              18 :             ENSURE(newReplaceChars.append(textstr->chars() + matchStart,
        2088                 :                                           textstr->chars() + matchLimit));
        2089              18 :             break;
        2090                 :           case '`':
        2091               0 :             ENSURE(newReplaceChars.append(textstr->chars(), textstr->chars() + matchStart));
        2092               0 :             break;
        2093                 :           case '\'':
        2094               0 :             ENSURE(newReplaceChars.append(textstr->chars() + matchLimit,
        2095                 :                                           textstr->chars() + textstr->length()));
        2096               0 :             break;
        2097                 :           default: /* The dollar we saw was not special (no matter what its mother told it). */
        2098               0 :             ENSURE(newReplaceChars.append(*it));
        2099               0 :             continue;
        2100                 :         }
        2101              18 :         ++it; /* We always eat an extra char in the above switch. */
        2102                 :     }
        2103                 :
        2104              18 :     JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart);
        2105              18 :     ENSURE(leftSide);
        2106                 :
        2107              18 :     JSString *newReplace = newReplaceChars.finishString();
        2108              18 :     ENSURE(newReplace);
        2109                 :
        2110              18 :     JS_ASSERT(textstr->length() >= matchLimit);
        2111                 :     JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit,
        2112              18 :                                                 textstr->length() - matchLimit);
        2113              18 :     ENSURE(rightSide);
        2114                 :
        2115              18 :     RopeBuilder builder(cx);
        2116              18 :     ENSURE(builder.append(leftSide) &&
        2117                 :            builder.append(newReplace) &&
        2118                 :            builder.append(rightSide));
        2119                 : #undef ENSURE
        2120                 :
        2121              18 :     args->rval() = StringValue(builder.result());
        2122              18 :     return true;
        2123                 : }
        2124                 :
        2125                 : static inline bool
        2126          341757 : str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata)
        2127                 : {
        2128          341757 :     if (!rdata.g.normalizeRegExp(cx, true, 2, args))
        2129               0 :         return false;
        2130                 :
        2131          341757 :     rdata.leftIndex = 0;
        2132          341757 :     rdata.calledBack = false;
        2133                 :
        2134          341757 :     RegExpStatics *res = cx->regExpStatics();
        2135          341757 :     RegExpShared &re = rdata.g.regExp();
        2136                 :
        2137                 :     Value tmp;
        2138          341757 :     if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
        2139               9 :         return false;
        2140                 :
        2141          341748 :     if (!rdata.calledBack) {
        2142                 :         /* Didn't match, so the string is unmodified. */
        2143          185805 :         args.rval() = StringValue(rdata.str);
        2144          185805 :         return true;
        2145                 :     }
        2146                 :
        2147                 :     JSSubString sub;
        2148          155943 :     res->getRightContext(&sub);
        2149          155943 :     if (!rdata.sb.append(sub.chars, sub.length))
        2150               0 :         return false;
        2151                 :
        2152          155943 :     JSString *retstr = rdata.sb.finishString();
        2153          155943 :     if (!retstr)
        2154               0 :         return false;
        2155                 :
        2156          155943 :     args.rval() = StringValue(retstr);
        2157          155943 :     return true;
        2158                 : }
        2159                 :
        2160                 : static inline bool
        2161               0 : str_replace_flat_lambda(JSContext *cx, CallArgs outerArgs, ReplaceData &rdata, const FlatMatch &fm)
        2162                 : {
        2163               0 :     JS_ASSERT(fm.match() >= 0);
        2164                 :
        2165               0 :     JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength());
        2166               0 :     if (!matchStr)
        2167               0 :         return false;
        2168                 :
        2169                 :     /* lambda(matchStr, matchStart, textstr) */
        2170                 :     static const uint32_t lambdaArgc = 3;
        2171               0 :     if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.args))
        2172               0 :         return false;
        2173                 :
        2174               0 :     CallArgs &args = rdata.args;
        2175               0 :     args.calleev().setObject(*rdata.lambda);
        2176               0 :     args.thisv().setUndefined();
        2177                 :
        2178               0 :     Value *sp = args.array();
        2179               0 :     sp[0].setString(matchStr);
        2180               0 :     sp[1].setInt32(fm.match());
        2181               0 :     sp[2].setString(rdata.str);
        2182                 :
        2183               0 :     if (!Invoke(cx, rdata.args))
        2184               0 :         return false;
        2185                 :
        2186               0 :     JSString *repstr = ToString(cx, args.rval());
        2187               0 :     if (!repstr)
        2188               0 :         return false;
        2189                 :
        2190               0 :     JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match());
        2191               0 :     if (!leftSide)
        2192               0 :         return false;
        2193                 :
        2194               0 :     size_t matchLimit = fm.match() + fm.patternLength();
        2195                 :     JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit,
        2196               0 :                                                 rdata.str->length() - matchLimit);
        2197               0 :     if (!rightSide)
        2198               0 :         return false;
        2199                 :
        2200               0 :     RopeBuilder builder(cx);
        2201               0 :     if (!(builder.append(leftSide) &&
        2202               0 :           builder.append(repstr) &&
        2203               0 :           builder.append(rightSide))) {
        2204               0 :         return false;
        2205                 :     }
        2206                 :
        2207               0 :     outerArgs.rval() = StringValue(builder.result());
        2208               0 :     return true;
        2209                 : }
        2210                 :
        2211                 : static const uint32_t ReplaceOptArg = 2;
        2212                 :
        2213                 : /*
        2214                 :  * Pattern match the script to check if it is is indexing into a particular
        2215                 :  * object, e.g. 'function(a) { return b[a]; }'. Avoid calling the script in
        2216                 :  * such cases, which are used by javascript packers (particularly the popular
        2217                 :  * Dean Edwards packer) to efficiently encode large scripts. We only handle the
        2218                 :  * code patterns generated by such packers here.
        2219                 :  */
        2220                 : static JSObject *
        2221            2241 : LambdaIsGetElem(JSObject &lambda, JSContext *cx)
        2222                 : {
        2223            2241 :     if (!lambda.isFunction())
        2224               0 :         return NULL;
        2225                 :
        2226            2241 :     JSFunction *fun = lambda.toFunction();
        2227            2241 :     if (!fun->isInterpreted())
        2228              90 :         return NULL;
        2229                 :
        2230            2151 :     JSScript *script = fun->script();
        2231            2151 :     jsbytecode *pc = script->code;
        2232                 :
        2233                 :     /* Look for an access to 'b' in the enclosing scope. */
        2234            2151 :     if (JSOp(*pc) != JSOP_NAME)
        2235            1737 :         return NULL;
        2236                 :     PropertyName *bname;
        2237             414 :     GET_NAME_FROM_BYTECODE(script, pc, 0, bname);
        2238             414 :     pc += JSOP_NAME_LENGTH;
        2239                 :
        2240                 :     /*
        2241                 :      * Do a conservative search for 'b' in the enclosing scope. Avoid using a
        2242                 :      * real name lookup since this can trigger observable effects.
        2243                 :      */
        2244                 :     Value b;
        2245             414 :     JSObject *scope = cx->stack.currentScriptedScopeChain();
        2246               0 :     while (true) {
        2247             414 :         if (scope->isCall()) {
        2248             387 :             if (scope->asCall().containsVarOrArg(bname, &b, cx))
        2249             387 :                 break;
        2250              27 :         } else if (scope->isBlock()) {
        2251              18 :             if (scope->asClonedBlock().containsVar(bname, &b, cx))
        2252              18 :                 break;
        2253                 :         } else {
        2254               9 :             return NULL;
        2255                 :         }
        2256               0 :         scope = &scope->asScope().enclosingScope();
        2257                 :     }
        2258                 :
        2259                 :     /* Look for 'a' to be the lambda's first argument. */
        2260             405 :     if (JSOp(*pc) != JSOP_GETARG || GET_SLOTNO(pc) != 0)
        2261             234 :         return NULL;
        2262             171 :     pc += JSOP_GETARG_LENGTH;
        2263                 :
        2264                 :     /* 'b[a]' */
        2265             171 :     if (JSOp(*pc) != JSOP_GETELEM)
        2266               0 :         return NULL;
        2267             171 :     pc += JSOP_GETELEM_LENGTH;
        2268                 :
        2269                 :     /* 'return b[a]' */
        2270             171 :     if (JSOp(*pc) != JSOP_RETURN)
        2271               0 :         return NULL;
        2272                 :
        2273                 :     /* 'b' must behave like a normal object. */
        2274             171 :     if (!b.isObject())
        2275               0 :         return NULL;
        2276                 :
        2277             171 :     JSObject &bobj = b.toObject();
        2278             171 :     Class *clasp = bobj.getClass();
        2279             171 :     if (!clasp->isNative() || clasp->ops.lookupProperty || clasp->ops.getProperty)
        2280               0 :         return NULL;
        2281                 :
        2282             171 :     return &bobj;
        2283                 : }
        2284                 :
        2285                 : JSBool
        2286          365526 : js::str_replace(JSContext *cx, unsigned argc, Value *vp)
        2287                 : {
        2288          365526 :     CallArgs args = CallArgsFromVp(argc, vp);
        2289                 :
        2290          731052 :     ReplaceData rdata(cx);
        2291          365526 :     rdata.str = ThisToStringForStringProto(cx, args);
        2292          365526 :     if (!rdata.str)
        2293               0 :         return false;
        2294                 :
        2295          365526 :     if (!rdata.g.init(cx, args))
        2296              18 :         return false;
        2297                 :
        2298                 :     /* Extract replacement string/function. */
        2299          365508 :     if (args.length() >= ReplaceOptArg && js_IsCallable(args[1])) {
        2300            2241 :         rdata.lambda = &args[1].toObject();
        2301            2241 :         rdata.elembase = NULL;
        2302            2241 :         rdata.repstr = NULL;
        2303            2241 :         rdata.dollar = rdata.dollarEnd = NULL;
        2304                 :
        2305            2241 :         if (JSObject *base = LambdaIsGetElem(*rdata.lambda, cx))
        2306             171 :             rdata.elembase = base;
        2307                 :     } else {
        2308          363267 :         rdata.lambda = NULL;
        2309          363267 :         rdata.elembase = NULL;
        2310          363267 :         rdata.repstr = ArgToRootedString(cx, args, 1);
        2311          363267 :         if (!rdata.repstr)
        2312               0 :             return false;
        2313                 :
        2314                 :         /* We're about to store pointers into the middle of our string. */
        2315          363267 :         JSFixedString *fixed = rdata.repstr->ensureFixed(cx);
        2316          363267 :         if (!fixed)
        2317               0 :             return false;
        2318          363267 :         rdata.dollarEnd = fixed->chars() + fixed->length();
        2319          363267 :         rdata.dollar = js_strchr_limit(fixed->chars(), '$', rdata.dollarEnd);
        2320                 :     }
        2321                 :
        2322                 :     /*
        2323                 :      * Unlike its |String.prototype| brethren, |replace| doesn't convert
        2324                 :      * its input to a regular expression. (Even if it contains metachars.)
        2325                 :      *
        2326                 :      * However, if the user invokes our (non-standard) |flags| argument
        2327                 :      * extension then we revert to creating a regular expression. Note that
        2328                 :      * this is observable behavior through the side-effect mutation of the
        2329                 :      * |RegExp| statics.
        2330                 :      */
        2331                 :
        2332          365508 :     const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, ReplaceOptArg, args.length(), false);
        2333          365508 :     if (!fm) {
        2334          341757 :         if (cx->isExceptionPending())  /* oom in RopeMatch in tryFlatMatch */
        2335               0 :             return false;
        2336          341757 :         return str_replace_regexp(cx, args, rdata);
        2337                 :     }
        2338                 :
        2339           23751 :     if (fm->match() < 0) {
        2340           20295 :         args.rval() = StringValue(rdata.str);
        2341           20295 :         return true;
        2342                 :     }
        2343                 :
        2344            3456 :     if (rdata.lambda)
        2345               0 :         return str_replace_flat_lambda(cx, args, rdata, *fm);
        2346                 :
        2347                 :     /*
        2348                 :      * Note: we could optimize the text.length == pattern.length case if we wanted,
        2349                 :      * even in the presence of dollar metachars.
        2350                 :      */
        2351            3456 :     if (rdata.dollar)
        2352              18 :         return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, &args);
        2353                 :
        2354            3438 :     return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, &args);
        2355                 : }
        2356                 :
        2357                 : class SplitMatchResult {
        2358                 :     size_t endIndex_;
        2359                 :     size_t length_;
        2360                 :
        2361                 :   public:
        2362           11997 :     void setFailure() {
        2363                 :         JS_STATIC_ASSERT(SIZE_MAX > JSString::MAX_LENGTH);
        2364           11997 :         endIndex_ = SIZE_MAX;
        2365           11997 :     }
        2366          937863 :     bool isFailure() const {
        2367          937863 :         return (endIndex_ == SIZE_MAX);
        2368                 :     }
        2369          308622 :     size_t endIndex() const {
        2370          308622 :         JS_ASSERT(!isFailure());
        2371          308622 :         return endIndex_;
        2372                 :     }
        2373          308622 :     size_t length() const {
        2374          308622 :         JS_ASSERT(!isFailure());
        2375          308622 :         return length_;
        2376                 :     }
        2377          308622 :     void setResult(size_t length, size_t endIndex) {
        2378          308622 :         length_ = length;
        2379          308622 :         endIndex_ = endIndex;
        2380          308622 :     }
        2381                 : };
        2382                 :
        2383                 : template<class Matcher>
        2384                 : static JSObject *
        2385           21096 : SplitHelper(JSContext *cx, JSLinearString *str, uint32_t limit, Matcher splitMatch, TypeObject *type)
        2386                 : {
        2387           21096 :     size_t strLength = str->length();
        2388                 :     SplitMatchResult result;
        2389                 :
        2390                 :     /* Step 11. */
        2391           21096 :     if (strLength == 0) {
        2392              90 :         if (!splitMatch(cx, str, 0, &result))
        2393               0 :             return NULL;
        2394                 :
        2395                 :         /*
        2396                 :          * NB: Unlike in the non-empty string case, it's perfectly fine
        2397                 :          *     (indeed the spec requires it) if we match at the end of the
        2398                 :          *     string.  Thus these cases should hold:
        2399                 :          *
        2400                 :          *   var a = "".split("");
        2401                 :          *   assertEq(a.length, 0);
        2402                 :          *   var b = "".split(/.?/);
        2403                 :          *   assertEq(b.length, 0);
        2404                 :          */
        2405              90 :         if (!result.isFailure())
        2406               0 :             return NewDenseEmptyArray(cx);
        2407                 :
        2408              90 :         Value v = StringValue(str);
        2409              90 :         return NewDenseCopiedArray(cx, 1, &v);
        2410                 :     }
        2411                 :
        2412                 :     /* Step 12. */
        2413           21006 :     size_t lastEndIndex = 0;
        2414           21006 :     size_t index = 0;
        2415                 :
        2416                 :     /* Step 13. */
        2417           42012 :     AutoValueVector splits(cx);
        2418                 :
        2419          350634 :     while (index < strLength) {
        2420                 :         /* Step 13(a). */
        2421          320529 :         if (!splitMatch(cx, str, index, &result))
        2422               0 :             return NULL;
        2423                 :
        2424                 :         /*
        2425                 :          * Step 13(b).
        2426                 :          *
        2427                 :          * Our match algorithm differs from the spec in that it returns the
        2428                 :          * next index at which a match happens.  If no match happens we're
        2429                 :          * done.
        2430                 :          *
        2431                 :          * But what if the match is at the end of the string (and the string is
        2432                 :          * not empty)?  Per 13(c)(ii) this shouldn't be a match, so we have to
        2433                 :          * specially exclude it.  Thus this case should hold:
        2434                 :          *
        2435                 :          *   var a = "abc".split(/\b/);
        2436                 :          *   assertEq(a.length, 1);
        2437                 :          *   assertEq(a[0], "abc");
        2438                 :          */
        2439          320529 :         if (result.isFailure())
        2440           11907 :             break;
        2441                 :
        2442                 :         /* Step 13(c)(i). */
        2443          308622 :         size_t sepLength = result.length();
        2444          308622 :         size_t endIndex = result.endIndex();
        2445          308622 :         if (sepLength == 0 && endIndex == strLength)
        2446               0 :             break;
        2447                 :
        2448                 :         /* Step 13(c)(ii). */
        2449          308622 :         if (endIndex == lastEndIndex) {
        2450           99000 :             index++;
        2451           99000 :             continue;
        2452                 :         }
        2453                 :
        2454                 :         /* Step 13(c)(iii). */
        2455          209622 :         JS_ASSERT(lastEndIndex < endIndex);
        2456          209622 :         JS_ASSERT(sepLength <= strLength);
        2457          209622 :         JS_ASSERT(lastEndIndex + sepLength <= endIndex);
        2458                 :
        2459                 :         /* Steps 13(c)(iii)(1-3). */
        2460          209622 :         size_t subLength = size_t(endIndex - sepLength - lastEndIndex);
        2461          209622 :         JSString *sub = js_NewDependentString(cx, str, lastEndIndex, subLength);
        2462          209622 :         if (!sub || !splits.append(StringValue(sub)))
        2463               0 :             return NULL;
        2464                 :
        2465                 :         /* Step 13(c)(iii)(4). */
        2466          209622 :         if (splits.length() == limit)
        2467               0 :             return NewDenseCopiedArray(cx, splits.length(), splits.begin());
        2468                 :
        2469                 :         /* Step 13(c)(iii)(5). */
        2470          209622 :         lastEndIndex = endIndex;
        2471                 :
        2472                 :         /* Step 13(c)(iii)(6-7). */
        2473                 :         if (Matcher::returnsCaptures) {
        2474           30483 :             RegExpStatics *res = cx->regExpStatics();
        2475           30483 :             for (size_t i = 0; i < res->parenCount(); i++) {
        2476                 :                 /* Steps 13(c)(iii)(7)(a-c). */
        2477               0 :                 if (res->pairIsPresent(i + 1)) {
        2478                 :                     JSSubString parsub;
        2479               0 :                     res->getParen(i + 1, &parsub);
        2480               0 :                     sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
        2481               0 :                     if (!sub || !splits.append(StringValue(sub)))
        2482               0 :                         return NULL;
        2483                 :                 } else {
        2484                 :                     /* Only string entries have been accounted for so far. */
        2485               0 :                     AddTypeProperty(cx, type, NULL, UndefinedValue());
        2486               0 :                     if (!splits.append(UndefinedValue()))
        2487               0 :                         return NULL;
        2488                 :                 }
        2489                 :
        2490                 :                 /* Step 13(c)(iii)(7)(d). */
        2491               0 :                 if (splits.length() == limit)
        2492               0 :                     return NewDenseCopiedArray(cx, splits.length(), splits.begin());
        2493                 :             }
        2494                 :         }
        2495                 :
        2496                 :         /* Step 13(c)(iii)(8). */
        2497          209622 :         index = lastEndIndex;
        2498                 :     }
        2499                 :
        2500                 :     /* Steps 14-15. */
        2501           21006 :     JSString *sub = js_NewDependentString(cx, str, lastEndIndex, strLength - lastEndIndex);
        2502           21006 :     if (!sub || !splits.append(StringValue(sub)))
        2503               0 :         return NULL;
        2504                 :
        2505                 :     /* Step 16. */
        2506           21006 :     return NewDenseCopiedArray(cx, splits.length(), splits.begin());
        2507                 : }
        2508                 :
        2509                 : /*
        2510                 :  * The SplitMatch operation from ES5 15.5.4.14 is implemented using different
        2511                 :  * paths for regular expression and string separators.
        2512                 :  *
        2513                 :  * The algorithm differs from the spec in that the we return the next index at
        2514                 :  * which a match happens.
        2515                 :  */
        2516                 : class SplitRegExpMatcher
        2517                 : {
        2518                 :     RegExpShared &re;
        2519                 :     RegExpStatics *res;
        2520                 :
        2521                 :   public:
        2522           11781 :     SplitRegExpMatcher(RegExpShared &re, RegExpStatics *res) : re(re), res(res) {}
        2523                 :
        2524                 :     static const bool returnsCaptures = true;
        2525                 :
        2526           42165 :     bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *result)
        2527                 :     {
        2528           42165 :         Value rval = UndefinedValue();
        2529           42165 :         const jschar *chars = str->chars();
        2530           42165 :         size_t length = str->length();
        2531           42165 :         if (!ExecuteRegExp(cx, res, re, str, chars, length, &index, RegExpTest, &rval))
        2532               0 :             return false;
        2533           42165 :         if (!rval.isTrue()) {
        2534           11682 :             result->setFailure();
        2535           11682 :             return true;
        2536                 :         }
        2537                 :         JSSubString sep;
        2538           30483 :         res->getLastMatch(&sep);
        2539                 :
        2540           30483 :         result->setResult(sep.length, index);
        2541           30483 :         return true;
        2542                 :     }
        2543                 : };
        2544                 :
        2545                 : class SplitStringMatcher
        2546                 : {
        2547                 :     const jschar *sepChars;
        2548                 :     size_t sepLength;
        2549                 :
        2550                 :   public:
        2551            9315 :     SplitStringMatcher(JSLinearString *sep) {
        2552            9315 :         sepChars = sep->chars();
        2553            9315 :         sepLength = sep->length();
        2554            9315 :     }
        2555                 :
        2556                 :     static const bool returnsCaptures = false;
        2557                 :
        2558          278454 :     bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *res)
        2559                 :     {
        2560          278454 :         JS_ASSERT(index == 0 || index < str->length());
        2561          278454 :         const jschar *chars = str->chars();
        2562          278454 :         int match = StringMatch(chars + index, str->length() - index, sepChars, sepLength);
        2563          278454 :         if (match == -1)
        2564             315 :             res->setFailure();
        2565                 :         else
        2566          278139 :             res->setResult(sepLength, index + match + sepLength);
        2567          278454 :         return true;
        2568                 :     }
        2569                 : };
        2570                 :
        2571                 : /* ES5 15.5.4.14 */
        2572                 : JSBool
        2573           21114 : js::str_split(JSContext *cx, unsigned argc, Value *vp)
        2574                 : {
        2575           21114 :     CallArgs args = CallArgsFromVp(argc, vp);
        2576                 :
        2577                 :     /* Steps 1-2. */
        2578           21114 :     JSString *str = ThisToStringForStringProto(cx, args);
        2579           21114 :     if (!str)
        2580               0 :         return false;
        2581                 :
        2582           21114 :     TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array);
        2583           21114 :     if (!type)
        2584               0 :         return false;
        2585           21114 :     AddTypeProperty(cx, type, NULL, Type::StringType());
        2586                 :
        2587                 :     /* Step 5: Use the second argument as the split limit, if given. */
        2588                 :     uint32_t limit;
        2589           21114 :     if (args.hasDefined(1)) {
        2590                 :         double d;
        2591               0 :         if (!ToNumber(cx, args[1], &d))
        2592               0 :             return false;
        2593               0 :         limit = js_DoubleToECMAUint32(d);
        2594                 :     } else {
        2595           21114 :         limit = UINT32_MAX;
        2596                 :     }
        2597                 :
        2598                 :     /* Step 8. */
        2599           42228 :     RegExpGuard re;
        2600           21114 :     JSLinearString *sepstr = NULL;
        2601           21114 :     bool sepDefined = args.hasDefined(0);
        2602           21114 :     if (sepDefined) {
        2603           21114 :         if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
        2604           11781 :             if (!RegExpToShared(cx, args[0].toObject(), &re))
        2605               0 :                 return false;
        2606                 :         } else {
        2607            9333 :             sepstr = ArgToRootedString(cx, args, 0);
        2608            9333 :             if (!sepstr)
        2609              18 :                 return false;
        2610                 :         }
        2611                 :     }
        2612                 :
        2613                 :     /* Step 9. */
        2614           21096 :     if (limit == 0) {
        2615               0 :         JSObject *aobj = NewDenseEmptyArray(cx);
        2616               0 :         if (!aobj)
        2617               0 :             return false;
        2618               0 :         aobj->setType(type);
        2619               0 :         args.rval() = ObjectValue(*aobj);
        2620               0 :         return true;
        2621                 :     }
        2622                 :
        2623                 :     /* Step 10. */
        2624           21096 :     if (!sepDefined) {
        2625               0 :         Value v = StringValue(str);
        2626               0 :         JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
        2627               0 :         if (!aobj)
        2628               0 :             return false;
        2629               0 :         aobj->setType(type);
        2630               0 :         args.rval() = ObjectValue(*aobj);
        2631               0 :         return true;
        2632                 :     }
        2633           21096 :     JSLinearString *strlin = str->ensureLinear(cx);
        2634           21096 :     if (!strlin)
        2635               0 :         return false;
        2636                 :
        2637                 :     /* Steps 11-15. */
        2638                 :     JSObject *aobj;
        2639           21096 :     if (!re.initialized())
        2640            9315 :         aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr), type);
        2641                 :     else
        2642           11781 :         aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(*re, cx->regExpStatics()), type);
        2643           21096 :     if (!aobj)
        2644               0 :         return false;
        2645                 :
        2646                 :     /* Step 16. */
        2647           21096 :     aobj->setType(type);
        2648           21096 :     args.rval() = ObjectValue(*aobj);
        2649           21096 :     return true;
        2650                 : }
        2651                 :
        2652                 : static JSBool
        2653           22563 : str_substr(JSContext *cx, unsigned argc, Value *vp)
        2654                 : {
        2655           22563 :     CallArgs args = CallArgsFromVp(argc, vp);
        2656           22563 :     JSString *str = ThisToStringForStringProto(cx, args);
        2657           22563 :     if (!str)
        2658               0 :         return false;
        2659                 :
        2660                 :     int32_t length, len, begin;
        2661           22563 :     if (args.length() > 0) {
        2662           22563 :         length = int32_t(str->length());
        2663           22563 :         if (!ValueToIntegerRange(cx, args[0], &begin))
        2664               0 :             return false;
        2665                 :
        2666           22563 :         if (begin >= length) {
        2667               0 :             str = cx->runtime->emptyString;
        2668               0 :             goto out;
        2669                 :         }
        2670           22563 :         if (begin < 0) {
        2671              36 :             begin += length; /* length + INT_MIN will always be less than 0 */
        2672              36 :             if (begin < 0)
        2673               9 :                 begin = 0;
        2674                 :         }
        2675                 :
        2676           22563 :         if (args.hasDefined(1)) {
        2677           22509 :             if (!ValueToIntegerRange(cx, args[1], &len))
        2678               0 :                 return false;
        2679                 :
        2680           22509 :             if (len <= 0) {
        2681               0 :                 str = cx->runtime->emptyString;
        2682               0 :                 goto out;
        2683                 :             }
        2684                 :
        2685           22509 :             if (uint32_t(length) < uint32_t(begin + len))
        2686              27 :                 len = length - begin;
        2687                 :         } else {
        2688              54 :             len = length - begin;
        2689                 :         }
        2690                 :
        2691           22563 :         str = js_NewDependentString(cx, str, size_t(begin), size_t(len));
        2692           22563 :         if (!str)
        2693               0 :             return false;
        2694                 :     }
        2695                 :
        2696                 : out:
        2697           22563 :     args.rval() = StringValue(str);
        2698           22563 :     return true;
        2699                 : }
        2700                 :
        2701                 : /*
        2702                 :  * Python-esque sequence operations.
        2703                 :  */
        2704                 : static JSBool
        2705             108 : str_concat(JSContext *cx, unsigned argc, Value *vp)
        2706                 : {
        2707             108 :     CallArgs args = CallArgsFromVp(argc, vp);
        2708             108 :     JSString *str = ThisToStringForStringProto(cx, args);
        2709             108 :     if (!str)
        2710               0 :         return false;
        2711                 :
        2712             207 :     for (unsigned i = 0; i < args.length(); i++) {
        2713             108 :         JSString *argStr = ToString(cx, args[i]);
        2714             108 :         if (!argStr)
        2715               9 :             return false;
        2716                 :
        2717              99 :         str = js_ConcatStrings(cx, str, argStr);
        2718              99 :         if (!str)
        2719               0 :             return false;
        2720                 :     }
        2721                 :
        2722              99 :     args.rval() = StringValue(str);
        2723              99 :     return true;
        2724                 : }
        2725                 :
        2726                 : static JSBool
        2727             567 : str_slice(JSContext *cx, unsigned argc, Value *vp)
        2728                 : {
        2729             567 :     CallArgs args = CallArgsFromVp(argc, vp);
        2730                 :
        2731             567 :     if (args.length() == 1 && args.thisv().isString() && args[0].isInt32()) {
        2732                 :         size_t begin, end, length;
        2733                 :
        2734               0 :         JSString *str = args.thisv().toString();
        2735               0 :         begin = args[0].toInt32();
        2736               0 :         end = str->length();
        2737               0 :         if (begin <= end) {
        2738               0 :             length = end - begin;
        2739               0 :             if (length == 0) {
        2740               0 :                 str = cx->runtime->emptyString;
        2741                 :             } else {
        2742                 :                 str = (length == 1)
        2743               0 :                       ? cx->runtime->staticStrings.getUnitStringForElement(cx, str, begin)
        2744               0 :                       : js_NewDependentString(cx, str, begin, length);
        2745               0 :                 if (!str)
        2746               0 :                     return false;
        2747                 :             }
        2748               0 :             args.rval() = StringValue(str);
        2749               0 :             return true;
        2750                 :         }
        2751                 :     }
        2752                 :
        2753             567 :     JSString *str = ThisToStringForStringProto(cx, args);
        2754             567 :     if (!str)
        2755               0 :         return false;
        2756                 :
        2757             567 :     if (args.length() != 0) {
        2758                 :         double begin, end, length;
        2759                 :
        2760             567 :         if (!ToInteger(cx, args[0], &begin))
        2761               0 :             return false;
        2762             567 :         length = str->length();
        2763             567 :         if (begin < 0) {
        2764               0 :             begin += length;
        2765               0 :             if (begin < 0)
        2766               0 :                 begin = 0;
        2767             567 :         } else if (begin > length) {
        2768               0 :             begin = length;
        2769                 :         }
        2770                 :
        2771             567 :         if (args.hasDefined(1)) {
        2772             567 :             if (!ToInteger(cx, args[1], &end))
        2773               0 :                 return false;
        2774             567 :             if (end < 0) {
        2775             567 :                 end += length;
        2776             567 :                 if (end < 0)
        2777               0 :                     end = 0;
        2778               0 :             } else if (end > length) {
        2779               0 :                 end = length;
        2780                 :             }
        2781             567 :             if (end < begin)
        2782               0 :                 end = begin;
        2783                 :         } else {
        2784               0 :             end = length;
        2785                 :         }
        2786                 :
        2787                 :         str = js_NewDependentString(cx, str,
        2788                 :                                     (size_t)begin,
        2789             567 :                                     (size_t)(end - begin));
        2790             567 :         if (!str)
        2791               0 :             return false;
        2792                 :     }
        2793             567 :     args.rval() = StringValue(str);
        2794             567 :     return true;
        2795                 : }
        2796                 :
        2797                 : #if JS_HAS_STR_HTML_HELPERS
        2798                 : /*
        2799                 :  * HTML composition aids.
        2800                 :  */
        2801                 : static bool
        2802               0 : tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end,
        2803                 :        CallReceiver call)
        2804                 : {
        2805               0 :     JSString *thisstr = ThisToStringForStringProto(cx, call);
        2806               0 :     if (!thisstr)
        2807               0 :         return false;
        2808                 :
        2809               0 :     JSLinearString *str = thisstr->ensureLinear(cx);
        2810               0 :     if (!str)
        2811               0 :         return false;
        2812                 :
        2813               0 :     if (!end)
        2814               0 :         end = begin;
        2815                 :
        2816               0 :     size_t beglen = strlen(begin);
        2817               0 :     size_t taglen = 1 + beglen + 1;                     /* '<begin' + '>' */
        2818               0 :     size_t parlen = 0; /* Avoid warning. */
        2819               0 :     if (param) {
        2820               0 :         parlen = param->length();
        2821               0 :         taglen += 2 + parlen + 1;                       /* '="param"' */
        2822                 :     }
        2823               0 :     size_t endlen = strlen(end);
        2824               0 :     taglen += str->length() + 2 + endlen + 1;           /* 'str</end>' */
        2825                 :
        2826               0 :     if (taglen >= ~(size_t)0 / sizeof(jschar)) {
        2827               0 :         js_ReportAllocationOverflow(cx);
        2828               0 :         return false;
        2829                 :     }
        2830                 :
        2831               0 :     jschar *tagbuf = (jschar *) cx->malloc_((taglen + 1) * sizeof(jschar));
        2832               0 :     if (!tagbuf)
        2833               0 :         return false;
        2834                 :
        2835               0 :     size_t j = 0;
        2836               0 :     tagbuf[j++] = '<';
        2837               0 :     for (size_t i = 0; i < beglen; i++)
        2838               0 :         tagbuf[j++] = (jschar)begin[i];
        2839               0 :     if (param) {
        2840               0 :         tagbuf[j++] = '=';
        2841               0 :         tagbuf[j++] = '"';
        2842               0 :         js_strncpy(&tagbuf[j], param->chars(), parlen);
        2843               0 :         j += parlen;
        2844               0 :         tagbuf[j++] = '"';
        2845                 :     }
        2846               0 :     tagbuf[j++] = '>';
        2847                 :
        2848               0 :     js_strncpy(&tagbuf[j], str->chars(), str->length());
        2849               0 :     j += str->length();
        2850               0 :     tagbuf[j++] = '<';
        2851               0 :     tagbuf[j++] = '/';
        2852               0 :     for (size_t i = 0; i < endlen; i++)
        2853               0 :         tagbuf[j++] = (jschar)end[i];
        2854               0 :     tagbuf[j++] = '>';
        2855               0 :     JS_ASSERT(j == taglen);
        2856               0 :     tagbuf[j] = 0;
        2857                 :
        2858               0 :     JSString *retstr = js_NewString(cx, tagbuf, taglen);
        2859               0 :     if (!retstr) {
        2860               0 :         Foreground::free_((char *)tagbuf);
        2861               0 :         return false;
        2862                 :     }
        2863               0 :     call.rval() = StringValue(retstr);
        2864               0 :     return true;
        2865                 : }
        2866                 :
        2867                 : static JSBool
        2868               0 : tagify_value(JSContext *cx, CallArgs args, const char *begin, const char *end)
        2869                 : {
        2870               0 :     JSLinearString *param = ArgToRootedString(cx, args, 0);
        2871               0 :     if (!param)
        2872               0 :         return false;
        2873                 :
        2874               0 :     return tagify(cx, begin, param, end, args);
        2875                 : }
        2876                 :
        2877                 : static JSBool
        2878               0 : str_bold(JSContext *cx, unsigned argc, Value *vp)
        2879                 : {
        2880               0 :     return tagify(cx, "b", NULL, NULL, CallReceiverFromVp(vp));
        2881                 : }
        2882                 :
        2883                 : static JSBool
        2884               0 : str_italics(JSContext *cx, unsigned argc, Value *vp)
        2885                 : {
        2886               0 :     return tagify(cx, "i", NULL, NULL, CallReceiverFromVp(vp));
        2887                 : }
        2888                 :
        2889                 : static JSBool
        2890               0 : str_fixed(JSContext *cx, unsigned argc, Value *vp)
        2891                 : {
        2892               0 :     return tagify(cx, "tt", NULL, NULL, CallReceiverFromVp(vp));
        2893                 : }
        2894                 :
        2895                 : static JSBool
        2896               0 : str_fontsize(JSContext *cx, unsigned argc, Value *vp)
        2897                 : {
        2898               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "font size", "font");
        2899                 : }
        2900                 :
        2901                 : static JSBool
        2902               0 : str_fontcolor(JSContext *cx, unsigned argc, Value *vp)
        2903                 : {
        2904               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "font color", "font");
        2905                 : }
        2906                 :
        2907                 : static JSBool
        2908               0 : str_link(JSContext *cx, unsigned argc, Value *vp)
        2909                 : {
        2910               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "a href", "a");
        2911                 : }
        2912                 :
        2913                 : static JSBool
        2914               0 : str_anchor(JSContext *cx, unsigned argc, Value *vp)
        2915                 : {
        2916               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "a name", "a");
        2917                 : }
        2918                 :
        2919                 : static JSBool
        2920               0 : str_strike(JSContext *cx, unsigned argc, Value *vp)
        2921                 : {
        2922               0 :     return tagify(cx, "strike", NULL, NULL, CallReceiverFromVp(vp));
        2923                 : }
        2924                 :
        2925                 : static JSBool
        2926               0 : str_small(JSContext *cx, unsigned argc, Value *vp)
        2927                 : {
        2928               0 :     return tagify(cx, "small", NULL, NULL, CallReceiverFromVp(vp));
        2929                 : }
        2930                 :
        2931                 : static JSBool
        2932               0 : str_big(JSContext *cx, unsigned argc, Value *vp)
        2933                 : {
        2934               0 :     return tagify(cx, "big", NULL, NULL, CallReceiverFromVp(vp));
        2935                 : }
        2936                 :
        2937                 : static JSBool
        2938               0 : str_blink(JSContext *cx, unsigned argc, Value *vp)
        2939                 : {
        2940               0 :     return tagify(cx, "blink", NULL, NULL, CallReceiverFromVp(vp));
        2941                 : }
        2942                 :
        2943                 : static JSBool
        2944               0 : str_sup(JSContext *cx, unsigned argc, Value *vp)
        2945                 : {
        2946               0 :     return tagify(cx, "sup", NULL, NULL, CallReceiverFromVp(vp));
        2947                 : }
        2948                 :
        2949                 : static JSBool
        2950               0 : str_sub(JSContext *cx, unsigned argc, Value *vp)
        2951                 : {
        2952               0 :     return tagify(cx, "sub", NULL, NULL, CallReceiverFromVp(vp));
        2953                 : }
        2954                 : #endif /* JS_HAS_STR_HTML_HELPERS */
        2955                 :
        2956                 : static JSFunctionSpec string_methods[] = {
        2957                 : #if JS_HAS_TOSOURCE
        2958                 :     JS_FN("quote",             str_quote,             0,JSFUN_GENERIC_NATIVE),
        2959                 :     JS_FN(js_toSource_str,     str_toSource,          0,0),
        2960                 : #endif
        2961                 :
        2962                 :     /* Java-like methods. */
        2963                 :     JS_FN(js_toString_str,     js_str_toString,       0,0),
        2964                 :     JS_FN(js_valueOf_str,      js_str_toString,       0,0),
        2965                 :     JS_FN("substring",         str_substring,         2,JSFUN_GENERIC_NATIVE),
        2966                 :     JS_FN("toLowerCase",       str_toLowerCase,       0,JSFUN_GENERIC_NATIVE),
        2967                 :     JS_FN("toUpperCase",       str_toUpperCase,       0,JSFUN_GENERIC_NATIVE),
        2968                 :     JS_FN("charAt",            js_str_charAt,         1,JSFUN_GENERIC_NATIVE),
        2969                 :     JS_FN("charCodeAt",        js_str_charCodeAt,     1,JSFUN_GENERIC_NATIVE),
        2970                 :     JS_FN("indexOf",           str_indexOf,           1,JSFUN_GENERIC_NATIVE),
        2971                 :     JS_FN("lastIndexOf",       str_lastIndexOf,       1,JSFUN_GENERIC_NATIVE),
        2972                 :     JS_FN("trim",              str_trim,              0,JSFUN_GENERIC_NATIVE),
        2973                 :     JS_FN("trimLeft",          str_trimLeft,          0,JSFUN_GENERIC_NATIVE),
        2974                 :     JS_FN("trimRight",         str_trimRight,         0,JSFUN_GENERIC_NATIVE),
        2975                 :     JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE),
        2976                 :     JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE),
        2977                 :     JS_FN("localeCompare",     str_localeCompare,     1,JSFUN_GENERIC_NATIVE),
        2978                 :
        2979                 :     /* Perl-ish methods (search is actually Python-esque). */
        2980                 :     JS_FN("match",             str_match,             1,JSFUN_GENERIC_NATIVE),
        2981                 :     JS_FN("search",            str_search,            1,JSFUN_GENERIC_NATIVE),
        2982                 :     JS_FN("replace",           str_replace,           2,JSFUN_GENERIC_NATIVE),
        2983                 :     JS_FN("split",             str_split,             2,JSFUN_GENERIC_NATIVE),
        2984                 :     JS_FN("substr",            str_substr,            2,JSFUN_GENERIC_NATIVE),
        2985                 :
        2986                 :     /* Python-esque sequence methods. */
        2987                 :     JS_FN("concat",            str_concat,            1,JSFUN_GENERIC_NATIVE),
        2988                 :     JS_FN("slice",             str_slice,             2,JSFUN_GENERIC_NATIVE),
        2989                 :
        2990                 :     /* HTML string methods. */
        2991                 : #if JS_HAS_STR_HTML_HELPERS
        2992                 :     JS_FN("bold",              str_bold,              0,0),
        2993                 :     JS_FN("italics",           str_italics,           0,0),
        2994                 :     JS_FN("fixed",             str_fixed,             0,0),
        2995                 :     JS_FN("fontsize",          str_fontsize,          1,0),
        2996                 :     JS_FN("fontcolor",         str_fontcolor,         1,0),
        2997                 :     JS_FN("link",              str_link,              1,0),
        2998                 :     JS_FN("anchor",            str_anchor,            1,0),
        2999                 :     JS_FN("strike",            str_strike,            0,0),
        3000                 :     JS_FN("small",             str_small,             0,0),
        3001                 :     JS_FN("big",               str_big,               0,0),
        3002                 :     JS_FN("blink",             str_blink,             0,0),
        3003                 :     JS_FN("sup",               str_sup,               0,0),
        3004                 :     JS_FN("sub",               str_sub,               0,0),
        3005                 : #endif
        3006                 :
        3007                 :     JS_FS_END
        3008                 : };
        3009                 :
        3010                 : JSBool
        3011          349096 : js_String(JSContext *cx, unsigned argc, Value *vp)
        3012                 : {
        3013          349096 :     CallArgs args = CallArgsFromVp(argc, vp);
        3014                 :
        3015                 :     JSString *str;
        3016          349096 :     if (args.length() > 0) {
        3017          348664 :         str = ToString(cx, args[0]);
        3018          348664 :         if (!str)
        3019               0 :             return false;
        3020                 :     } else {
        3021             432 :         str = cx->runtime->emptyString;
        3022                 :     }
        3023                 :
        3024          349096 :     if (IsConstructing(args)) {
        3025          199206 :         StringObject *strobj = StringObject::create(cx, str);
        3026          199206 :         if (!strobj)
        3027               0 :             return false;
        3028          199206 :         args.rval() = ObjectValue(*strobj);
        3029          199206 :         return true;
        3030                 :     }
        3031                 :
        3032          149890 :     args.rval() = StringValue(str);
        3033          149890 :     return true;
        3034                 : }
        3035                 :
        3036                 : JSBool
        3037         1219476 : js::str_fromCharCode(JSContext *cx, unsigned argc, Value *vp)
        3038                 : {
        3039         1219476 :     CallArgs args = CallArgsFromVp(argc, vp);
        3040                 :
        3041         1219476 :     JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
        3042         1219476 :     if (args.length() == 1) {
        3043                 :         uint16_t code;
        3044         1219476 :         if (!ValueToUint16(cx, args[0], &code))
        3045               9 :             return JS_FALSE;
        3046         1219467 :         if (StaticStrings::hasUnit(code)) {
        3047           44427 :             args.rval() = StringValue(cx->runtime->staticStrings.getUnit(code));
        3048           44427 :             return JS_TRUE;
        3049                 :         }
        3050         1175040 :         args[0].setInt32(code);
        3051                 :     }
        3052         1175040 :     jschar *chars = (jschar *) cx->malloc_((args.length() + 1) * sizeof(jschar));
        3053         1175040 :     if (!chars)
        3054               0 :         return JS_FALSE;
        3055         2350080 :     for (unsigned i = 0; i < args.length(); i++) {
        3056                 :         uint16_t code;
        3057         1175040 :         if (!ValueToUint16(cx, args[i], &code)) {
        3058               0 :             cx->free_(chars);
        3059               0 :             return JS_FALSE;
        3060                 :         }
        3061         1175040 :         chars[i] = (jschar)code;
        3062                 :     }
        3063         1175040 :     chars[args.length()] = 0;
        3064         1175040 :     JSString *str = js_NewString(cx, chars, args.length());
        3065         1175040 :     if (!str) {
        3066               0 :         cx->free_(chars);
        3067               0 :         return JS_FALSE;
        3068                 :     }
        3069                 :
        3070         1175040 :     args.rval() = StringValue(str);
        3071         1175040 :     return JS_TRUE;
        3072                 : }
        3073                 :
        3074                 : static JSFunctionSpec string_static_methods[] = {
        3075                 :     JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
        3076                 :     JS_FS_END
        3077                 : };
        3078                 :
        3079                 : Shape *
        3080            3721 : StringObject::assignInitialShape(JSContext *cx)
        3081                 : {
        3082            3721 :     JS_ASSERT(nativeEmpty());
        3083                 :
        3084            3721 :     return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
        3085            3721 :                            LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
        3086                 : }
        3087                 :
        3088                 : JSObject *
        3089            2266 : js_InitStringClass(JSContext *cx, JSObject *obj)
        3090                 : {
        3091            2266 :     JS_ASSERT(obj->isNative());
        3092                 :
        3093            2266 :     GlobalObject *global = &obj->asGlobal();
        3094                 :
        3095            2266 :     JSObject *proto = global->createBlankPrototype(cx, &StringClass);
        3096            2266 :     if (!proto || !proto->asString().init(cx, cx->runtime->emptyString))
        3097               0 :         return NULL;
        3098                 :
        3099                 :     /* Now create the String function. */
        3100            2266 :     JSFunction *ctor = global->createConstructor(cx, js_String, CLASS_ATOM(cx, String), 1);
        3101            2266 :     if (!ctor)
        3102               0 :         return NULL;
        3103                 :
        3104            2266 :     if (!LinkConstructorAndPrototype(cx, ctor, proto))
        3105               0 :         return NULL;
        3106                 :
        3107            4532 :     if (!DefinePropertiesAndBrand(cx, proto, NULL, string_methods) ||
        3108            2266 :         !DefinePropertiesAndBrand(cx, ctor, NULL, string_static_methods))
        3109                 :     {
        3110               0 :         return NULL;
        3111                 :     }
        3112                 :
        3113                 :     /* Capture normal data properties pregenerated for String objects. */
        3114            2266 :     TypeObject *type = proto->getNewType(cx);
        3115            2266 :     if (!type)
        3116               0 :         return NULL;
        3117            2266 :     AddTypeProperty(cx, type, "length", Type::Int32Type());
        3118                 :
        3119            2266 :     if (!DefineConstructorAndPrototype(cx, global, JSProto_String, ctor, proto))
        3120               0 :         return NULL;
        3121                 :
        3122                 :     /*
        3123                 :      * Define escape/unescape, the URI encode/decode functions, and maybe
        3124                 :      * uneval on the global object.
        3125                 :      */
        3126            2266 :     if (!JS_DefineFunctions(cx, global, string_functions))
        3127               0 :         return NULL;
        3128                 :
        3129            2266 :     return proto;
        3130                 : }
        3131                 :
        3132                 : JSFixedString *
        3133         6777523 : js_NewString(JSContext *cx, jschar *chars, size_t length)
        3134                 : {
        3135         6777523 :     JSFixedString *s = JSFixedString::new_(cx, chars, length);
        3136         6777523 :     if (s)
        3137         6777523 :         Probes::createString(cx, s, length);
        3138         6777523 :     return s;
        3139                 : }
        3140                 :
        3141                 : static JSInlineString *
        3142           84974 : NewShortString(JSContext *cx, const char *chars, size_t length)
        3143                 : {
        3144           84974 :     JS_ASSERT(JSShortString::lengthFits(length));
        3145           84974 :     JSInlineString *str = JSInlineString::lengthFits(length)
        3146                 :                           ? JSInlineString::new_(cx)
        3147           84974 :                           : JSShortString::new_(cx);
        3148           84974 :     if (!str)
        3149               0 :         return NULL;
        3150                 :
        3151           84974 :     jschar *storage = str->init(length);
        3152           84974 :     if (js_CStringsAreUTF8) {
        3153                 : #ifdef DEBUG
        3154               0 :         size_t oldLength = length;
        3155                 : #endif
        3156               0 :         if (!InflateUTF8StringToBuffer(cx, chars, length, storage, &length))
        3157               0 :             return NULL;
        3158               0 :         JS_ASSERT(length <= oldLength);
        3159               0 :         storage[length] = 0;
        3160               0 :         str->resetLength(length);
        3161                 :     } else {
        3162           84974 :         size_t n = length;
        3163           84974 :         jschar *p = storage;
        3164          837442 :         while (n--)
        3165          667494 :             *p++ = (unsigned char)*chars++;
        3166           84974 :         *p = 0;
        3167                 :     }
        3168           84974 :     Probes::createString(cx, str, length);
        3169           84974 :     return str;
        3170                 : }
        3171                 :
        3172                 : JSLinearString *
        3173         1933981 : js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t length)
        3174                 : {
        3175         1933981 :     if (length == 0)
        3176           40843 :         return cx->runtime->emptyString;
        3177                 :
        3178         1893138 :     JSLinearString *base = baseArg->ensureLinear(cx);
        3179         1893138 :     if (!base)
        3180               0 :         return NULL;
        3181                 :
        3182         1893138 :     if (start == 0 && length == base->length())
        3183           95011 :         return base;
        3184                 :
        3185         1798127 :     const jschar *chars = base->chars() + start;
        3186                 :
        3187         1798127 :     if (JSLinearString *staticStr = cx->runtime->staticStrings.lookup(chars, length))
        3188          934983 :         return staticStr;
        3189                 :
        3190          863144 :     JSLinearString *s = JSDependentString::new_(cx, base, chars, length);
        3191          863144 :     Probes::createString(cx, s, length);
        3192          863144 :     return s;
        3193                 : }
        3194                 :
        3195                 : JSFixedString *
        3196        95203845 : js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
        3197                 : {
        3198        95203845 :     if (JSShortString::lengthFits(n))
        3199        91949158 :         return NewShortString(cx, s, n);
        3200                 :
        3201         3254687 :     jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
        3202         3254687 :     if (!news)
        3203               0 :         return NULL;
        3204         3254687 :     js_strncpy(news, s, n);
        3205         3254687 :     news[n] = 0;
        3206         3254687 :     JSFixedString *str = js_NewString(cx, news, n);
        3207         3254687 :     if (!str)
        3208               0 :         cx->free_(news);
        3209         3254687 :     return str;
        3210                 : }
        3211                 :
        3212                 : JSFixedString *
        3213          212038 : js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
        3214                 : {
        3215          212038 :     if (JSShortString::lengthFits(n))
        3216           84974 :         return NewShortString(cx, s, n);
        3217                 :
        3218          127064 :     jschar *chars = InflateString(cx, s, &n);
        3219          127064 :     if (!chars)
        3220               0 :         return NULL;
        3221          127064 :     JSFixedString *str = js_NewString(cx, chars, n);
        3222          127064 :     if (!str)
        3223               0 :         cx->free_(chars);
        3224          127064 :     return str;
        3225                 : }
        3226                 :
        3227                 : JSFixedString *
        3228               0 : js_NewStringCopyZ(JSContext *cx, const jschar *s)
        3229                 : {
        3230               0 :     size_t n = js_strlen(s);
        3231               0 :     if (JSShortString::lengthFits(n))
        3232               0 :         return NewShortString(cx, s, n);
        3233                 :
        3234               0 :     size_t m = (n + 1) * sizeof(jschar);
        3235               0 :     jschar *news = (jschar *) cx->malloc_(m);
        3236               0 :     if (!news)
        3237               0 :         return NULL;
        3238               0 :     js_memcpy(news, s, m);
        3239               0 :     JSFixedString *str = js_NewString(cx, news, n);
        3240               0 :     if (!str)
        3241               0 :         cx->free_(news);
        3242               0 :     return str;
        3243                 : }
        3244                 :
        3245                 : JSFixedString *
        3246          212029 : js_NewStringCopyZ(JSContext *cx, const char *s)
        3247                 : {
        3248          212029 :     return js_NewStringCopyN(cx, s, strlen(s));
        3249                 : }
        3250                 :
        3251                 : const char *
        3252           20566 : js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool asSource)
        3253                 : {
        3254                 :     JSString *str;
        3255                 :
        3256           20566 :     str = (asSource ? js_ValueToSource : ToString)(cx, v);
        3257           20566 :     if (!str)
        3258               0 :         return NULL;
        3259           20566 :     str = js_QuoteString(cx, str, 0);
        3260           20566 :     if (!str)
        3261               0 :         return NULL;
        3262           20566 :     return bytes->encode(cx, str);
        3263                 : }
        3264                 :
        3265                 : JSString *
        3266         3838935 : js::ToStringSlow(JSContext *cx, const Value &arg)
        3267                 : {
        3268                 :     /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
        3269         3838935 :     JS_ASSERT(!arg.isString());
        3270                 :
        3271         3838935 :     Value v = arg;
        3272         3838935 :     if (!ToPrimitive(cx, JSTYPE_STRING, &v))
        3273            5433 :         return NULL;
        3274                 :
        3275                 :     JSString *str;
        3276         3833502 :     if (v.isString()) {
        3277           36229 :         str = v.toString();
        3278         3797273 :     } else if (v.isInt32()) {
        3279          877957 :         str = js_IntToString(cx, v.toInt32());
        3280         2919316 :     } else if (v.isDouble()) {
        3281         2848603 :         str = js_NumberToString(cx, v.toDouble());
        3282           70713 :     } else if (v.isBoolean()) {
        3283            3069 :         str = js_BooleanToString(cx, v.toBoolean());
        3284           67644 :     } else if (v.isNull()) {
        3285             873 :         str = cx->runtime->atomState.nullAtom;
        3286                 :     } else {
        3287           66771 :         str = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
        3288                 :     }
        3289         3833502 :     return str;
        3290                 : }
        3291                 :
        3292                 : JS_FRIEND_API(JSString *)
        3293           11323 : js_ValueToSource(JSContext *cx, const Value &v)
        3294                 : {
        3295           11323 :     JS_CHECK_RECURSION(cx, return NULL);
        3296                 :
        3297           11323 :     if (v.isUndefined())
        3298             351 :         return cx->runtime->atomState.void0Atom;
        3299           10972 :     if (v.isString())
        3300            2286 :         return js_QuoteString(cx, v.toString(), '"');
        3301            8686 :     if (v.isPrimitive()) {
        3302                 :         /* Special case to preserve negative zero, _contra_ toString. */
        3303            3798 :         if (v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble())) {
        3304                 :             /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
        3305                 :             static const jschar js_negzero_ucNstr[] = {'-', '0'};
        3306                 :
        3307               9 :             return js_NewStringCopyN(cx, js_negzero_ucNstr, 2);
        3308                 :         }
        3309            3789 :         return ToString(cx, v);
        3310                 :     }
        3311                 :
        3312            4888 :     Value rval = NullValue();
        3313                 :     Value fval;
        3314            4888 :     jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
        3315            4888 :     if (!js_GetMethod(cx, &v.toObject(), id, 0, &fval))
        3316               0 :         return NULL;
        3317            4888 :     if (js_IsCallable(fval)) {
        3318            4888 :         if (!Invoke(cx, v, fval, 0, NULL, &rval))
        3319              18 :             return NULL;
        3320                 :     }
        3321                 :
        3322            4870 :     return ToString(cx, rval);
        3323                 : }
        3324                 :
        3325                 : namespace js {
        3326                 :
        3327                 : bool
        3328         1632114 : EqualStrings(JSContext *cx, JSString *str1, JSString *str2, bool *result)
        3329                 : {
        3330         1632114 :     if (str1 == str2) {
        3331          130408 :         *result = true;
        3332          130408 :         return true;
        3333                 :     }
        3334                 :
        3335         1501706 :     size_t length1 = str1->length();
        3336         1501706 :     if (length1 != str2->length()) {
        3337          731666 :         *result = false;
        3338          731666 :         return true;
        3339                 :     }
        3340                 :
        3341          770040 :     JSLinearString *linear1 = str1->ensureLinear(cx);
        3342          770040 :     if (!linear1)
        3343               0 :         return false;
        3344          770040 :     JSLinearString *linear2 = str2->ensureLinear(cx);
        3345          770040 :     if (!linear2)
        3346               0 :         return false;
        3347                 :
        3348          770040 :     *result = PodEqual(linear1->chars(), linear2->chars(), length1);
        3349          770040 :     return true;
        3350                 : }
        3351                 :
        3352                 : bool
        3353          116020 : EqualStrings(JSLinearString *str1, JSLinearString *str2)
        3354                 : {
        3355          116020 :     if (str1 == str2)
        3356             432 :         return true;
        3357                 :
        3358          115588 :     size_t length1 = str1->length();
        3359          115588 :     if (length1 != str2->length())
        3360           39708 :         return false;
        3361                 :
        3362           75880 :     return PodEqual(str1->chars(), str2->chars(), length1);
        3363                 : }
        3364                 :
        3365                 : }  /* namespace js */
        3366                 :
        3367                 : namespace js {
        3368                 :
        3369                 : static bool
        3370          331072 : CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
        3371                 : {
        3372          331072 :     JS_ASSERT(str1);
        3373          331072 :     JS_ASSERT(str2);
        3374                 :
        3375          331072 :     if (str1 == str2) {
        3376            2554 :         *result = 0;
        3377            2554 :         return true;
        3378                 :     }
        3379                 :
        3380          328518 :     const jschar *s1 = str1->getChars(cx);
        3381          328518 :     if (!s1)
        3382               0 :         return false;
        3383                 :
        3384          328518 :     const jschar *s2 = str2->getChars(cx);
        3385          328518 :     if (!s2)
        3386               0 :         return false;
        3387                 :
        3388          328518 :     return CompareChars(s1, str1->length(), s2, str2->length(), result);
        3389                 : }
        3390                 :
        3391                 : bool
        3392          331072 : CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
        3393                 : {
        3394          331072 :     return CompareStringsImpl(cx, str1, str2, result);
        3395                 : }
        3396                 :
        3397                 : }  /* namespace js */
        3398                 :
        3399                 : namespace js {
        3400                 :
        3401                 : bool
        3402            9440 : StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
        3403                 : {
        3404            9440 :     size_t length = strlen(asciiBytes);
        3405                 : #ifdef DEBUG
        3406          154818 :     for (size_t i = 0; i != length; ++i)
        3407          145378 :         JS_ASSERT(unsigned(asciiBytes[i]) <= 127);
        3408                 : #endif
        3409            9440 :     if (length != str->length())
        3410            4681 :         return false;
        3411            4759 :     const jschar *chars = str->chars();
        3412           76467 :     for (size_t i = 0; i != length; ++i) {
        3413           71719 :         if (unsigned(asciiBytes[i]) != unsigned(chars[i]))
        3414              11 :             return false;
        3415                 :     }
        3416            4748 :     return true;
        3417                 : }
        3418                 :
        3419                 : } /* namespacejs */
        3420                 :
        3421                 : size_t
        3422           57843 : js_strlen(const jschar *s)
        3423                 : {
        3424                 :     const jschar *t;
        3425                 :
        3426         1132549 :     for (t = s; *t != 0; t++)
        3427         1074706 :         continue;
        3428           57843 :     return (size_t)(t - s);
        3429                 : }
        3430                 :
        3431                 : jschar *
        3432               0 : js_strchr(const jschar *s, jschar c)
        3433                 : {
        3434               0 :     while (*s != 0) {
        3435               0 :         if (*s == c)
        3436               0 :             return (jschar *)s;
        3437               0 :         s++;
        3438                 :     }
        3439               0 :     return NULL;
        3440                 : }
        3441                 :
        3442                 : jschar *
        3443          366779 : js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
        3444                 : {
        3445         3721900 :     while (s < limit) {
        3446         2989197 :         if (*s == c)
        3447             855 :             return (jschar *)s;
        3448         2988342 :         s++;
        3449                 :     }
        3450          365924 :     return NULL;
        3451                 : }
        3452                 :
        3453                 : namespace js {
        3454                 :
        3455                 : jschar *
        3456         2993986 : InflateString(JSContext *cx, const char *bytes, size_t *lengthp, FlationCoding fc)
        3457                 : {
        3458                 :     size_t nchars;
        3459                 :     jschar *chars;
        3460         2993986 :     size_t nbytes = *lengthp;
        3461                 :
        3462         2993986 :     if (js_CStringsAreUTF8 || fc == CESU8Encoding) {
        3463           13186 :         if (!InflateUTF8StringToBuffer(cx, bytes, nbytes, NULL, &nchars, fc))
        3464               0 :             goto bad;
        3465           13186 :         chars = (jschar *) cx->malloc_((nchars + 1) * sizeof (jschar));
        3466           13186 :         if (!chars)
        3467               0 :             goto bad;
        3468           13186 :         JS_ALWAYS_TRUE(InflateUTF8StringToBuffer(cx, bytes, nbytes, chars, &nchars, fc));
        3469                 :     } else {
        3470         2980800 :         nchars = nbytes;
        3471         2980800 :         chars = (jschar *) cx->malloc_((nchars + 1) * sizeof(jschar));
        3472         2980800 :         if (!chars)
        3473               0 :             goto bad;
        3474       181660130 :         for (size_t i = 0; i < nchars; i++)
        3475       178679330 :             chars[i] = (unsigned char) bytes[i];
        3476                 :     }
        3477         2993986 :     *lengthp = nchars;
        3478         2993986 :     chars[nchars] = 0;
        3479         2993986 :     return chars;
        3480                 :
        3481                 :   bad:
        3482                 :     /*
        3483                 :      * For compatibility with callers of JS_DecodeBytes we must zero lengthp
        3484                 :      * on errors.
        3485                 :      */
        3486               0 :     *lengthp = 0;
        3487               0 :     return NULL;
        3488                 : }
        3489                 :
        3490                 : /*
        3491                 :  * May be called with null cx.
        3492                 :  */
        3493                 : char *
        3494           67839 : DeflateString(JSContext *cx, const jschar *chars, size_t nchars)
        3495                 : {
        3496                 :     size_t nbytes, i;
        3497                 :     char *bytes;
        3498                 :
        3499           67839 :     if (js_CStringsAreUTF8) {
        3500              20 :         nbytes = GetDeflatedStringLength(cx, chars, nchars);
        3501              20 :         if (nbytes == (size_t) -1)
        3502               0 :             return NULL;
        3503              20 :         bytes = (char *) (cx ? cx->malloc_(nbytes + 1) : OffTheBooks::malloc_(nbytes + 1));
        3504              20 :         if (!bytes)
        3505               0 :             return NULL;
        3506              20 :         JS_ALWAYS_TRUE(DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes));
        3507                 :     } else {
        3508           67819 :         nbytes = nchars;
        3509           67819 :         bytes = (char *) (cx ? cx->malloc_(nbytes + 1) : OffTheBooks::malloc_(nbytes + 1));
        3510           67819 :         if (!bytes)
        3511               0 :             return NULL;
        3512         1105879 :         for (i = 0; i < nbytes; i++)
        3513         1038060 :             bytes[i] = (char) chars[i];
        3514                 :     }
        3515           67839 :     bytes[nbytes] = 0;
        3516           67839 :     return bytes;
        3517                 : }
        3518                 :
        3519                 : size_t
        3520             417 : GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars)
        3521                 : {
        3522             417 :     if (!js_CStringsAreUTF8)
        3523             396 :         return nchars;
        3524                 :
        3525              21 :     return GetDeflatedUTF8StringLength(cx, chars, nchars);
        3526                 : }
        3527                 :
        3528                 : /*
        3529                 :  * May be called with null cx through public API, see below.
        3530                 :  */
        3531                 : size_t
        3532              21 : GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars,
        3533                 :                                 size_t nchars, FlationCoding fc)
        3534                 : {
        3535                 :     size_t nbytes;
        3536                 :     const jschar *end;
        3537                 :     unsigned c, c2;
        3538                 :     char buffer[10];
        3539              21 :     bool useCESU8 = fc == CESU8Encoding;
        3540                 :
        3541              21 :     nbytes = nchars;
        3542             524 :     for (end = chars + nchars; chars != end; chars++) {
        3543             503 :         c = *chars;
        3544             503 :         if (c < 0x80)
        3545             502 :             continue;
        3546               1 :         if (0xD800 <= c && c <= 0xDFFF && !useCESU8) {
        3547                 :             /* Surrogate pair. */
        3548               1 :             chars++;
        3549                 :
        3550                 :             /* nbytes sets 1 length since this is surrogate pair. */
        3551               1 :             nbytes--;
        3552               1 :             if (c >= 0xDC00 || chars == end)
        3553                 :                 goto bad_surrogate;
        3554               1 :             c2 = *chars;
        3555               1 :             if (c2 < 0xDC00 || c2 > 0xDFFF)
        3556                 :                 goto bad_surrogate;
        3557               1 :             c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
        3558                 :         }
        3559               1 :         c >>= 11;
        3560               1 :         nbytes++;
        3561               4 :         while (c) {
        3562               2 :             c >>= 5;
        3563               2 :             nbytes++;
        3564                 :         }
        3565                 :     }
        3566              21 :     return nbytes;
        3567                 :
        3568                 :   bad_surrogate:
        3569               0 :     if (cx) {
        3570               0 :         JS_snprintf(buffer, 10, "0x%x", c);
        3571                 :         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
        3572               0 :                                      NULL, JSMSG_BAD_SURROGATE_CHAR, buffer);
        3573                 :     }
        3574               0 :     return (size_t) -1;
        3575                 : }
        3576                 :
        3577                 : bool
        3578             417 : DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen,
        3579                 :                           char *dst, size_t *dstlenp)
        3580                 : {
        3581                 :     size_t dstlen, i;
        3582                 :
        3583             417 :     dstlen = *dstlenp;
        3584             417 :     if (!js_CStringsAreUTF8) {
        3585             396 :         if (srclen > dstlen) {
        3586               0 :             for (i = 0; i < dstlen; i++)
        3587               0 :                 dst[i] = (char) src[i];
        3588               0 :             if (cx) {
        3589                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3590               0 :                                      JSMSG_BUFFER_TOO_SMALL);
        3591                 :             }
        3592               0 :             return JS_FALSE;
        3593                 :         }
        3594           25947 :         for (i = 0; i < srclen; i++)
        3595           25551 :             dst[i] = (char) src[i];
        3596             396 :         *dstlenp = srclen;
        3597             396 :         return JS_TRUE;
        3598                 :     }
        3599                 :
        3600              21 :     return DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp);
        3601                 : }
        3602                 :
        3603                 : bool
        3604              21 : DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen,
        3605                 :                               char *dst, size_t *dstlenp, FlationCoding fc)
        3606                 : {
        3607                 :     size_t i, utf8Len;
        3608                 :     jschar c, c2;
        3609                 :     uint32_t v;
        3610                 :     uint8_t utf8buf[6];
        3611                 :
        3612              21 :     bool useCESU8 = fc == CESU8Encoding;
        3613              21 :     size_t dstlen = *dstlenp;
        3614              21 :     size_t origDstlen = dstlen;
        3615                 :
        3616             545 :     while (srclen) {
        3617             503 :         c = *src++;
        3618             503 :         srclen--;
        3619             503 :         if ((c >= 0xDC00) && (c <= 0xDFFF) && !useCESU8)
        3620               0 :             goto badSurrogate;
        3621             503 :         if (c < 0xD800 || c > 0xDBFF || useCESU8) {
        3622             502 :             v = c;
        3623                 :         } else {
        3624               1 :             if (srclen < 1)
        3625               0 :                 goto badSurrogate;
        3626               1 :             c2 = *src;
        3627               1 :             if ((c2 < 0xDC00) || (c2 > 0xDFFF))
        3628                 :                 goto badSurrogate;
        3629               1 :             src++;
        3630               1 :             srclen--;
        3631               1 :             v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
        3632                 :         }
        3633             503 :         if (v < 0x0080) {
        3634                 :             /* no encoding necessary - performance hack */
        3635             502 :             if (dstlen == 0)
        3636               0 :                 goto bufferTooSmall;
        3637             502 :             *dst++ = (char) v;
        3638             502 :             utf8Len = 1;
        3639                 :         } else {
        3640               1 :             utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
        3641               1 :             if (utf8Len > dstlen)
        3642               0 :                 goto bufferTooSmall;
        3643               5 :             for (i = 0; i < utf8Len; i++)
        3644               4 :                 *dst++ = (char) utf8buf[i];
        3645                 :         }
        3646             503 :         dstlen -= utf8Len;
        3647                 :     }
        3648              21 :     *dstlenp = (origDstlen - dstlen);
        3649              21 :     return JS_TRUE;
        3650                 :
        3651                 : badSurrogate:
        3652               0 :     *dstlenp = (origDstlen - dstlen);
        3653                 :     /* Delegate error reporting to the measurement function. */
        3654               0 :     if (cx)
        3655               0 :         GetDeflatedStringLength(cx, src - 1, srclen + 1);
        3656               0 :     return JS_FALSE;
        3657                 :
        3658                 : bufferTooSmall:
        3659               0 :     *dstlenp = (origDstlen - dstlen);
        3660               0 :     if (cx) {
        3661                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3662               0 :                              JSMSG_BUFFER_TOO_SMALL);
        3663                 :     }
        3664               0 :     return JS_FALSE;
        3665                 : }
        3666                 :
        3667                 : bool
        3668        28488972 : InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen,
        3669                 :                           jschar *dst, size_t *dstlenp)
        3670                 : {
        3671                 :     size_t dstlen, i;
        3672                 :
        3673        28488972 :     if (js_CStringsAreUTF8)
        3674           55017 :         return InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp);
        3675                 :
        3676        28433955 :     if (dst) {
        3677        28433955 :         dstlen = *dstlenp;
        3678        28433955 :         if (srclen > dstlen) {
        3679               0 :             for (i = 0; i < dstlen; i++)
        3680               0 :                 dst[i] = (unsigned char) src[i];
        3681               0 :             if (cx) {
        3682                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3683               0 :                                      JSMSG_BUFFER_TOO_SMALL);
        3684                 :             }
        3685               0 :             return JS_FALSE;
        3686                 :         }
        3687       242830820 :         for (i = 0; i < srclen; i++)
        3688       214396865 :             dst[i] = (unsigned char) src[i];
        3689                 :     }
        3690        28433955 :     *dstlenp = srclen;
        3691        28433955 :     return JS_TRUE;
        3692                 : }
        3693                 :
        3694                 : bool
        3695          119671 : InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen,
        3696                 :                               jschar *dst, size_t *dstlenp, FlationCoding fc)
        3697                 : {
        3698                 :     size_t dstlen, origDstlen, offset, j, n;
        3699                 :     uint32_t v;
        3700                 :
        3701          119671 :     dstlen = dst ? *dstlenp : (size_t) -1;
        3702          119671 :     origDstlen = dstlen;
        3703          119671 :     offset = 0;
        3704          119671 :     bool useCESU8 = fc == CESU8Encoding;
        3705                 :
        3706        27508801 :     while (srclen) {
        3707        27269459 :         v = (uint8_t) *src;
        3708        27269459 :         n = 1;
        3709        27269459 :         if (v & 0x80) {
        3710             729 :             while (v & (0x80 >> n))
        3711             315 :                 n++;
        3712             207 :             if (n > srclen)
        3713               0 :                 goto bufferTooSmall;
        3714             207 :             if (n == 1 || n > 4)
        3715                 :                 goto badCharacter;
        3716             522 :             for (j = 1; j < n; j++) {
        3717             315 :                 if ((src[j] & 0xC0) != 0x80)
        3718               0 :                     goto badCharacter;
        3719                 :             }
        3720             207 :             v = Utf8ToOneUcs4Char((uint8_t *)src, n);
        3721             207 :             if (v >= 0x10000 && !useCESU8) {
        3722               0 :                 v -= 0x10000;
        3723               0 :                 if (v > 0xFFFFF || dstlen < 2) {
        3724               0 :                     *dstlenp = (origDstlen - dstlen);
        3725               0 :                     if (cx) {
        3726                 :                         char buffer[10];
        3727               0 :                         JS_snprintf(buffer, 10, "0x%x", v + 0x10000);
        3728                 :                         JS_ReportErrorFlagsAndNumber(cx,
        3729                 :                                                      JSREPORT_ERROR,
        3730                 :                                                      js_GetErrorMessage, NULL,
        3731                 :                                                      JSMSG_UTF8_CHAR_TOO_LARGE,
        3732               0 :                                                      buffer);
        3733                 :                     }
        3734               0 :                     return JS_FALSE;
        3735                 :                 }
        3736               0 :                 if (dst) {
        3737               0 :                     *dst++ = (jschar)((v >> 10) + 0xD800);
        3738               0 :                     v = (jschar)((v & 0x3FF) + 0xDC00);
        3739                 :                 }
        3740               0 :                 dstlen--;
        3741                 :             }
        3742                 :         }
        3743        27269459 :         if (!dstlen)
        3744               0 :             goto bufferTooSmall;
        3745        27269459 :         if (dst)
        3746        27006319 :             *dst++ = (jschar) v;
        3747        27269459 :         dstlen--;
        3748        27269459 :         offset += n;
        3749        27269459 :         src += n;
        3750        27269459 :         srclen -= n;
        3751                 :     }
        3752          119671 :     *dstlenp = (origDstlen - dstlen);
        3753          119671 :     return JS_TRUE;
        3754                 :
        3755                 : badCharacter:
        3756               0 :     *dstlenp = (origDstlen - dstlen);
        3757               0 :     if (cx) {
        3758                 :         char buffer[10];
        3759               0 :         JS_snprintf(buffer, 10, "%d", offset);
        3760                 :         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
        3761                 :                                      js_GetErrorMessage, NULL,
        3762                 :                                      JSMSG_MALFORMED_UTF8_CHAR,
        3763               0 :                                      buffer);
        3764                 :     }
        3765               0 :     return JS_FALSE;
        3766                 :
        3767                 : bufferTooSmall:
        3768               0 :     *dstlenp = (origDstlen - dstlen);
        3769               0 :     if (cx) {
        3770                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3771               0 :                              JSMSG_BUFFER_TOO_SMALL);
        3772                 :     }
        3773               0 :     return JS_FALSE;
        3774                 : }
        3775                 :
        3776                 : } /* namepsace js */
        3777                 :
        3778                 : const jschar js_uriReservedPlusPound_ucstr[] =
        3779                 :     {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};
        3780                 : const jschar js_uriUnescaped_ucstr[] =
        3781                 :     {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        3782                 :      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        3783                 :      'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        3784                 :      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
        3785                 :      'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        3786                 :      '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
        3787                 :
        3788                 : #define ____ false
        3789                 :
        3790                 : /*
        3791                 :  * Identifier start chars:
        3792                 :  * -      36:    $
        3793                 :  * -  65..90: A..Z
        3794                 :  * -      95:    _
        3795                 :  * - 97..122: a..z
        3796                 :  */
        3797                 : const bool js_isidstart[] = {
        3798                 : /*       0     1     2     3     4     5     6     7     8     9  */
        3799                 : /*  0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3800                 : /*  1 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3801                 : /*  2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3802                 : /*  3 */ ____, ____, ____, ____, ____, ____, true, ____, ____, ____,
        3803                 : /*  4 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3804                 : /*  5 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3805                 : /*  6 */ ____, ____, ____, ____, ____, true, true, true, true, true,
        3806                 : /*  7 */ true, true, true, true, true, true, true, true, true, true,
        3807                 : /*  8 */ true, true, true, true, true, true, true, true, true, true,
        3808                 : /*  9 */ true, ____, ____, ____, ____, true, ____, true, true, true,
        3809                 : /* 10 */ true, true, true, true, true, true, true, true, true, true,
        3810                 : /* 11 */ true, true, true, true, true, true, true, true, true, true,
        3811                 : /* 12 */ true, true, true, ____, ____, ____, ____, ____
        3812                 : };
        3813                 :
        3814                 : /*
        3815                 :  * Identifier chars:
        3816                 :  * -      36:    $
        3817                 :  * -  48..57: 0..9
        3818                 :  * -  65..90: A..Z
        3819                 :  * -      95:    _
        3820                 :  * - 97..122: a..z
        3821                 :  */
        3822                 : const bool js_isident[] = {
        3823                 : /*       0     1     2     3     4     5     6     7     8     9  */
        3824                 : /*  0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3825                 : /*  1 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3826                 : /*  2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3827                 : /*  3 */ ____, ____, ____, ____, ____, ____, true, ____, ____, ____,
        3828                 : /*  4 */ ____, ____, ____, ____, ____, ____, ____, ____, true, true,
        3829                 : /*  5 */ true, true, true, true, true, true, true, true, ____, ____,
        3830                 : /*  6 */ ____, ____, ____, ____, ____, true, true, true, true, true,
        3831                 : /*  7 */ true, true, true, true, true, true, true, true, true, true,
        3832                 : /*  8 */ true, true, true, true, true, true, true, true, true, true,
        3833                 : /*  9 */ true, ____, ____, ____, ____, true, ____, true, true, true,
        3834                 : /* 10 */ true, true, true, true, true, true, true, true, true, true,
        3835                 : /* 11 */ true, true, true, true, true, true, true, true, true, true,
        3836                 : /* 12 */ true, true, true, ____, ____, ____, ____, ____
        3837                 : };
        3838                 :
        3839                 : /* Whitespace chars: '\t', '\n', '\v', '\f', '\r', ' '. */
        3840                 : const bool js_isspace[] = {
        3841                 : /*       0     1     2     3     4     5     6     7     8     9  */
        3842                 : /*  0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, true,
        3843                 : /*  1 */ true, true, true, true, ____, ____, ____, ____, ____, ____,
        3844                 : /*  2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3845                 : /*  3 */ ____, ____, true, ____, ____, ____, ____, ____, ____, ____,
        3846                 : /*  4 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3847                 : /*  5 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3848                 : /*  6 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3849                 : /*  7 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3850                 : /*  8 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3851                 : /*  9 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3852                 : /* 10 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3853                 : /* 11 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3854                 : /* 12 */ ____, ____, ____, ____, ____, ____, ____, ____
        3855                 : };
        3856                 :
        3857                 : #undef ____
        3858                 :
        3859                 : #define URI_CHUNK 64U
        3860                 :
        3861                 : static inline bool
        3862             162 : TransferBufferToString(JSContext *cx, StringBuffer &sb, Value *rval)
        3863                 : {
        3864             162 :     JSString *str = sb.finishString();
        3865             162 :     if (!str)
        3866               0 :         return false;
        3867             162 :     rval->setString(str);
        3868             162 :     return true;
        3869                 : }
        3870                 :
        3871                 : /*
        3872                 :  * ECMA 3, 15.1.3 URI Handling Function Properties
        3873                 :  *
        3874                 :  * The following are implementations of the algorithms
        3875                 :  * given in the ECMA specification for the hidden functions
        3876                 :  * 'Encode' and 'Decode'.
        3877                 :  */
        3878                 : static JSBool
        3879               0 : Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
        3880                 :        const jschar *unescapedSet2, Value *rval)
        3881                 : {
        3882                 :     static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
        3883                 :
        3884               0 :     size_t length = str->length();
        3885               0 :     const jschar *chars = str->getChars(cx);
        3886               0 :     if (!chars)
        3887               0 :         return JS_FALSE;
        3888                 :
        3889               0 :     if (length == 0) {
        3890               0 :         rval->setString(cx->runtime->emptyString);
        3891               0 :         return JS_TRUE;
        3892                 :     }
        3893                 :
        3894               0 :     StringBuffer sb(cx);
        3895                 :     jschar hexBuf[4];
        3896               0 :     hexBuf[0] = '%';
        3897               0 :     hexBuf[3] = 0;
        3898               0 :     for (size_t k = 0; k < length; k++) {
        3899               0 :         jschar c = chars[k];
        3900               0 :         if (js_strchr(unescapedSet, c) ||
        3901               0 :             (unescapedSet2 && js_strchr(unescapedSet2, c))) {
        3902               0 :             if (!sb.append(c))
        3903               0 :                 return JS_FALSE;
        3904                 :         } else {
        3905               0 :             if ((c >= 0xDC00) && (c <= 0xDFFF)) {
        3906                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3907               0 :                                  JSMSG_BAD_URI, NULL);
        3908               0 :                 return JS_FALSE;
        3909                 :             }
        3910                 :             uint32_t v;
        3911               0 :             if (c < 0xD800 || c > 0xDBFF) {
        3912               0 :                 v = c;
        3913                 :             } else {
        3914               0 :                 k++;
        3915               0 :                 if (k == length) {
        3916                 :                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3917               0 :                                      JSMSG_BAD_URI, NULL);
        3918               0 :                     return JS_FALSE;
        3919                 :                 }
        3920               0 :                 jschar c2 = chars[k];
        3921               0 :                 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
        3922                 :                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3923               0 :                                      JSMSG_BAD_URI, NULL);
        3924               0 :                     return JS_FALSE;
        3925                 :                 }
        3926               0 :                 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
        3927                 :             }
        3928                 :             uint8_t utf8buf[4];
        3929               0 :             size_t L = js_OneUcs4ToUtf8Char(utf8buf, v);
        3930               0 :             for (size_t j = 0; j < L; j++) {
        3931               0 :                 hexBuf[1] = HexDigits[utf8buf[j] >> 4];
        3932               0 :                 hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
        3933               0 :                 if (!sb.append(hexBuf, 3))
        3934               0 :                     return JS_FALSE;
        3935                 :             }
        3936                 :         }
        3937                 :     }
        3938                 :
        3939               0 :     return TransferBufferToString(cx, sb, rval);
        3940                 : }
        3941                 :
        3942                 : static JSBool
        3943             171 : Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval)
        3944                 : {
        3945             171 :     size_t length = str->length();
        3946             171 :     const jschar *chars = str->getChars(cx);
        3947             171 :     if (!chars)
        3948               0 :         return JS_FALSE;
        3949                 :
        3950             171 :     if (length == 0) {
        3951               0 :         rval->setString(cx->runtime->emptyString);
        3952               0 :         return JS_TRUE;
        3953                 :     }
        3954                 :
        3955             342 :     StringBuffer sb(cx);
        3956             333 :     for (size_t k = 0; k < length; k++) {
        3957             171 :         jschar c = chars[k];
        3958             171 :         if (c == '%') {
        3959               9 :             size_t start = k;
        3960               9 :             if ((k + 2) >= length)
        3961               9 :                 goto report_bad_uri;
        3962               0 :             if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
        3963                 :                 goto report_bad_uri;
        3964               0 :             uint32_t B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
        3965               0 :             k += 2;
        3966               0 :             if (!(B & 0x80)) {
        3967               0 :                 c = (jschar)B;
        3968                 :             } else {
        3969               0 :                 int n = 1;
        3970               0 :                 while (B & (0x80 >> n))
        3971               0 :                     n++;
        3972               0 :                 if (n == 1 || n > 4)
        3973                 :                     goto report_bad_uri;
        3974                 :                 uint8_t octets[4];
        3975               0 :                 octets[0] = (uint8_t)B;
        3976               0 :                 if (k + 3 * (n - 1) >= length)
        3977               0 :                     goto report_bad_uri;
        3978               0 :                 for (int j = 1; j < n; j++) {
        3979               0 :                     k++;
        3980               0 :                     if (chars[k] != '%')
        3981               0 :                         goto report_bad_uri;
        3982               0 :                     if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
        3983                 :                         goto report_bad_uri;
        3984               0 :                     B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
        3985               0 :                     if ((B & 0xC0) != 0x80)
        3986               0 :                         goto report_bad_uri;
        3987               0 :                     k += 2;
        3988               0 :                     octets[j] = (char)B;
        3989                 :                 }
        3990               0 :                 uint32_t v = Utf8ToOneUcs4Char(octets, n);
        3991               0 :                 if (v >= 0x10000) {
        3992               0 :                     v -= 0x10000;
        3993               0 :                     if (v > 0xFFFFF)
        3994               0 :                         goto report_bad_uri;
        3995               0 :                     c = (jschar)((v & 0x3FF) + 0xDC00);
        3996               0 :                     jschar H = (jschar)((v >> 10) + 0xD800);
        3997               0 :                     if (!sb.append(H))
        3998               0 :                         return JS_FALSE;
        3999                 :                 } else {
        4000               0 :                     c = (jschar)v;
        4001                 :                 }
        4002                 :             }
        4003               0 :             if (js_strchr(reservedSet, c)) {
        4004               0 :                 if (!sb.append(chars + start, k - start + 1))
        4005               0 :                     return JS_FALSE;
        4006                 :             } else {
        4007               0 :                 if (!sb.append(c))
        4008               0 :                     return JS_FALSE;
        4009                 :             }
        4010                 :         } else {
        4011             162 :             if (!sb.append(c))
        4012               0 :                 return JS_FALSE;
        4013                 :         }
        4014                 :     }
        4015                 :
        4016             162 :     return TransferBufferToString(cx, sb, rval);
        4017                 :
        4018                 :   report_bad_uri:
        4019               9 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
        4020                 :     /* FALL THROUGH */
        4021                 :
        4022               9 :     return JS_FALSE;
        4023                 : }
        4024                 :
        4025                 : static JSBool
        4026             171 : str_decodeURI(JSContext *cx, unsigned argc, Value *vp)
        4027                 : {
        4028             171 :     CallArgs args = CallArgsFromVp(argc, vp);
        4029             171 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4030             171 :     if (!str)
        4031               0 :         return false;
        4032                 :
        4033                 :     Value result;
        4034             171 :     if (!Decode(cx, str, js_uriReservedPlusPound_ucstr, &result))
        4035               9 :         return false;
        4036                 :
        4037             162 :     args.rval() = result;
        4038             162 :     return true;
        4039                 : }
        4040                 :
        4041                 : static JSBool
        4042               0 : str_decodeURI_Component(JSContext *cx, unsigned argc, Value *vp)
        4043                 : {
        4044               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        4045               0 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4046               0 :     if (!str)
        4047               0 :         return false;
        4048                 :
        4049                 :     Value result;
        4050               0 :     if (!Decode(cx, str, js_empty_ucstr, &result))
        4051               0 :         return false;
        4052                 :
        4053               0 :     args.rval() = result;
        4054               0 :     return true;
        4055                 : }
        4056                 :
        4057                 : static JSBool
        4058               0 : str_encodeURI(JSContext *cx, unsigned argc, Value *vp)
        4059                 : {
        4060               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        4061               0 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4062               0 :     if (!str)
        4063               0 :         return false;
        4064                 :
        4065                 :     Value result;
        4066               0 :     if (!Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, &result))
        4067               0 :         return false;
        4068                 :
        4069               0 :     args.rval() = result;
        4070               0 :     return true;
        4071                 : }
        4072                 :
        4073                 : static JSBool
        4074               0 : str_encodeURI_Component(JSContext *cx, unsigned argc, Value *vp)
        4075                 : {
        4076               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        4077               0 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4078               0 :     if (!str)
        4079               0 :         return false;
        4080                 :
        4081                 :     Value result;
        4082               0 :     if (!Encode(cx, str, js_uriUnescaped_ucstr, NULL, &result))
        4083               0 :         return false;
        4084                 :
        4085               0 :     args.rval() = result;
        4086               0 :     return true;
        4087                 : }
        4088                 :
        4089                 : /*
        4090                 :  * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
        4091                 :  * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
        4092                 :  */
        4093                 : int
        4094               1 : js_OneUcs4ToUtf8Char(uint8_t *utf8Buffer, uint32_t ucs4Char)
        4095                 : {
        4096               1 :     int utf8Length = 1;
        4097                 :
        4098               1 :     JS_ASSERT(ucs4Char <= 0x10FFFF);
        4099               1 :     if (ucs4Char < 0x80) {
        4100               0 :         *utf8Buffer = (uint8_t)ucs4Char;
        4101                 :     } else {
        4102                 :         int i;
        4103               1 :         uint32_t a = ucs4Char >> 11;
        4104               1 :         utf8Length = 2;
        4105               4 :         while (a) {
        4106               2 :             a >>= 5;
        4107               2 :             utf8Length++;
        4108                 :         }
        4109               1 :         i = utf8Length;
        4110               5 :         while (--i) {
        4111               3 :             utf8Buffer[i] = (uint8_t)((ucs4Char & 0x3F) | 0x80);
        4112               3 :             ucs4Char >>= 6;
        4113                 :         }
        4114               1 :         *utf8Buffer = (uint8_t)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
        4115                 :     }
        4116               1 :     return utf8Length;
        4117                 : }
        4118                 :
        4119                 : /*
        4120                 :  * Convert a utf8 character sequence into a UCS-4 character and return that
        4121                 :  * character.  It is assumed that the caller already checked that the sequence
        4122                 :  * is valid.
        4123                 :  */
        4124                 : static uint32_t
        4125             207 : Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length)
        4126                 : {
        4127             207 :     JS_ASSERT(1 <= utf8Length && utf8Length <= 4);
        4128                 :
        4129             207 :     if (utf8Length == 1) {
        4130               0 :         JS_ASSERT(!(*utf8Buffer & 0x80));
        4131               0 :         return *utf8Buffer;
        4132                 :     }
        4133                 :
        4134                 :     /* from Unicode 3.1, non-shortest form is illegal */
        4135                 :     static const uint32_t minucs4Table[] = { 0x80, 0x800, 0x10000 };
        4136                 :
        4137               0 :     JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7 - utf8Length)))) ==
        4138             207 :               (0x100 - (1 << (8 - utf8Length))));
        4139             207 :     uint32_t ucs4Char = *utf8Buffer++ & ((1 << (7 - utf8Length)) - 1);
        4140             207 :     uint32_t minucs4Char = minucs4Table[utf8Length - 2];
        4141             729 :     while (--utf8Length) {
        4142             315 :         JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
        4143             315 :         ucs4Char = (ucs4Char << 6) | (*utf8Buffer++ & 0x3F);
        4144                 :     }
        4145                 :
        4146             207 :     if (JS_UNLIKELY(ucs4Char < minucs4Char || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF)))
        4147               0 :         return INVALID_UTF8;
        4148                 :
        4149             207 :     return ucs4Char;
        4150                 : }
        4151                 :
        4152                 : namespace js {
        4153                 :
        4154                 : size_t
        4155          444636 : PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32_t quote)
        4156                 : {
        4157                 :     enum {
        4158                 :         STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
        4159                 :     } state;
        4160                 :
        4161          444636 :     JS_ASSERT(quote == 0 || quote == '\'' || quote == '"');
        4162          444636 :     JS_ASSERT_IF(!buffer, bufferSize == 0);
        4163          444636 :     JS_ASSERT_IF(fp, !buffer);
        4164                 :
        4165          444636 :     if (bufferSize == 0)
        4166               0 :         buffer = NULL;
        4167                 :     else
        4168          444636 :         bufferSize--;
        4169                 :
        4170          444636 :     const jschar *chars = str->chars();
        4171          444636 :     const jschar *charsEnd = chars + str->length();
        4172          444636 :     size_t n = 0;
        4173          444636 :     state = FIRST_QUOTE;
        4174          444636 :     unsigned shift = 0;
        4175          444636 :     unsigned hex = 0;
        4176          444636 :     unsigned u = 0;
        4177          444636 :     char c = 0;  /* to quell GCC warnings */
        4178                 :
        4179         4460948 :     for (;;) {
        4180         4905584 :         switch (state) {
        4181                 :           case STOP:
        4182          444636 :             goto stop;
        4183                 :           case FIRST_QUOTE:
        4184          444636 :             state = CHARS;
        4185          444636 :             goto do_quote;
        4186                 :           case LAST_QUOTE:
        4187          444636 :             state = STOP;
        4188                 :           do_quote:
        4189          889272 :             if (quote == 0)
        4190          889272 :                 continue;
        4191               0 :             c = (char)quote;
        4192               0 :             break;
        4193                 :           case CHARS:
        4194         3570380 :             if (chars == charsEnd) {
        4195          444636 :                 state = LAST_QUOTE;
        4196          444636 :                 continue;
        4197                 :             }
        4198         3125744 :             u = *chars++;
        4199         3125744 :             if (u < ' ') {
        4200             360 :                 if (u != 0) {
        4201             350 :                     const char *escape = strchr(js_EscapeMap, (int)u);
        4202             350 :                     if (escape) {
        4203             100 :                         u = escape[1];
        4204             100 :                         goto do_escape;
        4205                 :                     }
        4206                 :                 }
        4207             260 :                 goto do_hex_escape;
        4208                 :             }
        4209         3125384 :             if (u < 127) {
        4210         3125374 :                 if (u == quote || u == '\\')
        4211                 :                     goto do_escape;
        4212         3125366 :                 c = (char)u;
        4213              10 :             } else if (u < 0x100) {
        4214              10 :                 goto do_hex_escape;
        4215                 :             } else {
        4216               0 :                 shift = 16;
        4217               0 :                 hex = u;
        4218               0 :                 u = 'u';
        4219               0 :                 goto do_escape;
        4220                 :             }
        4221         3125366 :             break;
        4222                 :           do_hex_escape:
        4223             270 :             shift = 8;
        4224             270 :             hex = u;
        4225             270 :             u = 'x';
        4226                 :           do_escape:
        4227             378 :             c = '\\';
        4228             378 :             state = ESCAPE_START;
        4229             378 :             break;
        4230                 :           case ESCAPE_START:
        4231             378 :             JS_ASSERT(' ' <= u && u < 127);
        4232             378 :             c = (char)u;
        4233             378 :             state = ESCAPE_MORE;
        4234             378 :             break;
        4235                 :           case ESCAPE_MORE:
        4236             918 :             if (shift == 0) {
        4237             378 :                 state = CHARS;
        4238             378 :                 continue;
        4239                 :             }
        4240             540 :             shift -= 4;
        4241             540 :             u = 0xF & (hex >> shift);
        4242             540 :             c = (char)(u + (u < 10 ? '0' : 'A' - 10));
        4243             540 :             break;
        4244                 :         }
        4245         3126662 :         if (buffer) {
        4246         3126662 :             JS_ASSERT(n <= bufferSize);
        4247         3126662 :             if (n != bufferSize) {
        4248         3126662 :                 buffer[n] = c;
        4249                 :             } else {
        4250               0 :                 buffer[n] = '\0';
        4251               0 :                 buffer = NULL;
        4252                 :             }
        4253               0 :         } else if (fp) {
        4254               0 :             if (fputc(c, fp) < 0)
        4255               0 :                 return size_t(-1);
        4256                 :         }
        4257         3126662 :         n++;
        4258                 :     }
        4259                 :   stop:
        4260          444636 :     if (buffer)
        4261          444636 :         buffer[n] = '\0';
        4262          444636 :     return n;
        4263                 : }
        4264                 :
        4265                 : } /* namespace js */

Generated by: LCOV version 1.7