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: | 2118 | 1507 | 71.2 %
| Date: | 2012-06-02 | Functions: | 137 | 114 | 83.2 %

           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                 :
          80                 : #include "jsinferinlines.h"
          81                 : #include "jsobjinlines.h"
          82                 : #include "jsstrinlines.h"
          83                 : #include "jsautooplen.h"        // generated headers last
          84                 :
          85                 : #include "vm/MethodGuard-inl.h"
          86                 : #include "vm/RegExpObject-inl.h"
          87                 : #include "vm/RegExpStatics-inl.h"
          88                 : #include "vm/StringObject-inl.h"
          89                 : #include "vm/String-inl.h"
          90                 : #include "vm/StringBuffer-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         1224278 : ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno)
          99                 : {
         100         1224278 :     if (argno >= args.length())
         101              27 :         return cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
         102                 :
         103         1224251 :     Value &arg = args[argno];
         104         1224251 :     JSString *str = ToString(cx, arg);
         105         1224251 :     if (!str)
         106              72 :         return NULL;
         107                 :
         108         1224179 :     arg = StringValue(str);
         109         1224179 :     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             314 : str_escape(JSContext *cx, unsigned argc, Value *vp)
         140                 : {
         141             314 :     CallArgs args = CallArgsFromVp(argc, vp);
         142                 :
         143                 :     const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
         144             314 :                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
         145                 :
         146             314 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
         147             314 :     if (!str)
         148               0 :         return false;
         149                 :
         150             314 :     size_t length = str->length();
         151             314 :     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             314 :     size_t count = 0;
         167           80698 :     for (size_t i = 0; i < sizeof(shouldPassThrough); i++) {
         168           80384 :         if (shouldPassThrough[i]) {
         169           21666 :             count++;
         170                 :         }
         171                 :     }
         172             314 :     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             314 :     size_t newlength = length;
         178           13192 :     for (size_t i = 0; i < length; i++) {
         179           12878 :         jschar ch = chars[i];
         180           12878 :         if (ch < 128 && shouldPassThrough[ch])
         181            9126 :             continue;
         182                 :
         183                 :         /* The character will be encoded as %XX or %uXXXX. */
         184            3752 :         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            3752 :         if (newlength < length) {
         191               0 :             js_ReportAllocationOverflow(cx);
         192               0 :             return false;
         193                 :         }
         194                 :     }
         195                 :
         196             314 :     if (newlength >= ~(size_t)0 / sizeof(jschar)) {
         197               0 :         js_ReportAllocationOverflow(cx);
         198               0 :         return false;
         199                 :     }
         200                 :
         201             314 :     jschar *newchars = (jschar *) cx->malloc_((newlength + 1) * sizeof(jschar));
         202             314 :     if (!newchars)
         203               0 :         return false;
         204                 :     size_t i, ni;
         205           13192 :     for (i = 0, ni = 0; i < length; i++) {
         206           12878 :         jschar ch = chars[i];
         207           12878 :         if (ch < 128 && shouldPassThrough[ch]) {
         208            9126 :             newchars[ni++] = ch;
         209            3752 :         } else if (ch < 256) {
         210             564 :             newchars[ni++] = '%';
         211             564 :             newchars[ni++] = digits[ch >> 4];
         212             564 :             newchars[ni++] = digits[ch & 0xF];
         213                 :         } else {
         214            3188 :             newchars[ni++] = '%';
         215            3188 :             newchars[ni++] = 'u';
         216            3188 :             newchars[ni++] = digits[ch >> 12];
         217            3188 :             newchars[ni++] = digits[(ch & 0xF00) >> 8];
         218            3188 :             newchars[ni++] = digits[(ch & 0xF0) >> 4];
         219            3188 :             newchars[ni++] = digits[ch & 0xF];
         220                 :         }
         221                 :     }
         222             314 :     JS_ASSERT(ni == newlength);
         223             314 :     newchars[newlength] = 0;
         224                 :
         225             314 :     JSString *retstr = js_NewString(cx, newchars, newlength);
         226             314 :     if (!retstr) {
         227               0 :         cx->free_(newchars);
         228               0 :         return false;
         229                 :     }
         230                 :
         231             314 :     args.rval() = StringValue(retstr);
         232             314 :     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            4646 : str_uneval(JSContext *cx, unsigned argc, Value *vp)
         362                 : {
         363            4646 :     CallArgs args = CallArgsFromVp(argc, vp);
         364            4646 :     JSString *str = js_ValueToSource(cx, args.length() != 0 ? args[0] : UndefinedValue());
         365            4646 :     if (!str)
         366               0 :         return false;
         367                 :
         368            4646 :     args.rval() = StringValue(str);
         369            4646 :     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           46206 : str_enumerate(JSContext *cx, JSObject *obj)
         404                 : {
         405           46206 :     JSString *str = obj->asString().unbox();
         406          503749 :     for (size_t i = 0, length = str->length(); i < length; i++) {
         407          457543 :         JSString *str1 = js_NewDependentString(cx, str, i, 1);
         408          457543 :         if (!str1)
         409               0 :             return false;
         410          457543 :         if (!obj->defineElement(cx, i, StringValue(str1),
         411                 :                                 JS_PropertyStub, JS_StrictPropertyStub,
         412          457543 :                                 STRING_ELEMENT_ATTRS)) {
         413               0 :             return false;
         414                 :         }
         415                 :     }
         416                 :
         417           46206 :     return true;
         418                 : }
         419                 :
         420                 : static JSBool
         421          254783 : str_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
         422                 :             JSObject **objp)
         423                 : {
         424          254783 :     if (!JSID_IS_INT(id))
         425          236237 :         return JS_TRUE;
         426                 :
         427           18546 :     JSString *str = obj->asString().unbox();
         428                 :
         429           18546 :     int32_t slot = JSID_TO_INT(id);
         430           18546 :     if ((size_t)slot < str->length()) {
         431           12494 :         JSString *str1 = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(slot));
         432           12494 :         if (!str1)
         433               0 :             return JS_FALSE;
         434           12494 :         if (!obj->defineElement(cx, uint32_t(slot), StringValue(str1), NULL, NULL,
         435           12494 :                                 STRING_ELEMENT_ATTRS)) {
         436               0 :             return JS_FALSE;
         437                 :         }
         438           12494 :         *objp = obj;
         439                 :     }
         440           18546 :     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         2277123 : ThisToStringForStringProto(JSContext *cx, CallReceiver call)
         464                 : {
         465         2277123 :     JS_CHECK_RECURSION(cx, return NULL);
         466                 :
         467         2277123 :     if (call.thisv().isString())
         468         2273157 :         return call.thisv().toString();
         469                 :
         470            3966 :     if (call.thisv().isObject()) {
         471            3966 :         JSObject *obj = &call.thisv().toObject();
         472            6744 :         if (obj->isString() &&
         473                 :             ClassMethodIsNative(cx, obj,
         474                 :                                 &StringClass,
         475            2778 :                                 ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
         476            2778 :                                 js_str_toString))
         477                 :         {
         478            2778 :             JSString *str = obj->asString().unbox();
         479            2778 :             call.thisv().setString(str);
         480            2778 :             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             128 : str_toSource(JSContext *cx, unsigned argc, Value *vp)
         518                 : {
         519             128 :     CallArgs args = CallArgsFromVp(argc, vp);
         520                 :
         521                 :     JSString *str;
         522                 :     bool ok;
         523             128 :     if (!BoxedPrimitiveMethodGuard(cx, args, str_toSource, &str, &ok))
         524              63 :         return ok;
         525                 :
         526              65 :     str = js_QuoteString(cx, str, '"');
         527              65 :     if (!str)
         528               0 :         return false;
         529                 :
         530             130 :     StringBuffer sb(cx);
         531              65 :     if (!sb.append("(new String(") || !sb.append(str) || !sb.append("))"))
         532               0 :         return false;
         533                 :
         534              65 :     str = sb.finishString();
         535              65 :     if (!str)
         536               0 :         return false;
         537              65 :     args.rval() = StringValue(str);
         538              65 :     return true;
         539                 : }
         540                 :
         541                 : #endif /* JS_HAS_TOSOURCE */
         542                 :
         543                 : JSBool
         544            5296 : js_str_toString(JSContext *cx, unsigned argc, Value *vp)
         545                 : {
         546            5296 :     CallArgs args = CallArgsFromVp(argc, vp);
         547                 :
         548                 :     JSString *str;
         549                 :     bool ok;
         550            5296 :     if (!BoxedPrimitiveMethodGuard(cx, args, js_str_toString, &str, &ok))
         551              63 :         return ok;
         552                 :
         553            5233 :     args.rval() = StringValue(str);
         554            5233 :     return true;
         555                 : }
         556                 :
         557                 : /*
         558                 :  * Java-like string native methods.
         559                 :  */
         560                 :
         561                 : JS_ALWAYS_INLINE bool
         562          690961 : ValueToIntegerRange(JSContext *cx, const Value &v, int32_t *out)
         563                 : {
         564          690961 :     if (v.isInt32()) {
         565          690951 :         *out = v.toInt32();
         566                 :     } else {
         567                 :         double d;
         568              10 :         if (!ToInteger(cx, v, &d))
         569               0 :             return false;
         570              10 :         if (d > INT32_MAX)
         571               0 :             *out = INT32_MAX;
         572              10 :         else if (d < INT32_MIN)
         573               0 :             *out = INT32_MIN;
         574                 :         else
         575              10 :             *out = int32_t(d);
         576                 :     }
         577                 :
         578          690961 :     return true;
         579                 : }
         580                 :
         581                 : static JSBool
         582          355110 : str_substring(JSContext *cx, unsigned argc, Value *vp)
         583                 : {
         584          355110 :     CallArgs args = CallArgsFromVp(argc, vp);
         585                 :
         586          355110 :     JSString *str = ThisToStringForStringProto(cx, args);
         587          355110 :     if (!str)
         588               0 :         return false;
         589                 :
         590                 :     int32_t length, begin, end;
         591          355110 :     if (args.length() > 0) {
         592          355110 :         end = length = int32_t(str->length());
         593                 :
         594          355110 :         if (!ValueToIntegerRange(cx, args[0], &begin))
         595               0 :             return false;
         596                 :
         597          355110 :         if (begin < 0)
         598               0 :             begin = 0;
         599          355110 :         else if (begin > length)
         600              45 :             begin = length;
         601                 :
         602          355110 :         if (args.hasDefined(1)) {
         603          274174 :             if (!ValueToIntegerRange(cx, args[1], &end))
         604               0 :                 return false;
         605                 :
         606          274174 :             if (end > length) {
         607             335 :                 end = length;
         608                 :             } else {
         609          273839 :                 if (end < 0)
         610             511 :                     end = 0;
         611          273839 :                 if (end < begin) {
         612               0 :                     int32_t tmp = begin;
         613               0 :                     begin = end;
         614               0 :                     end = tmp;
         615                 :                 }
         616                 :             }
         617                 :         }
         618                 :
         619          355110 :         str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin));
         620          355110 :         if (!str)
         621               0 :             return false;
         622                 :     }
         623                 :
         624          355110 :     args.rval() = StringValue(str);
         625          355110 :     return true;
         626                 : }
         627                 :
         628                 : JSString* JS_FASTCALL
         629          118340 : js_toLowerCase(JSContext *cx, JSString *str)
         630                 : {
         631          118340 :     size_t n = str->length();
         632          118340 :     const jschar *s = str->getChars(cx);
         633          118340 :     if (!s)
         634               0 :         return NULL;
         635                 :
         636          118340 :     jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
         637          118340 :     if (!news)
         638               0 :         return NULL;
         639         1268669 :     for (size_t i = 0; i < n; i++)
         640         1150329 :         news[i] = unicode::ToLowerCase(s[i]);
         641          118340 :     news[n] = 0;
         642          118340 :     str = js_NewString(cx, news, n);
         643          118340 :     if (!str) {
         644               0 :         cx->free_(news);
         645               0 :         return NULL;
         646                 :     }
         647          118340 :     return str;
         648                 : }
         649                 :
         650                 : static inline bool
         651          118340 : ToLowerCaseHelper(JSContext *cx, CallReceiver call)
         652                 : {
         653          118340 :     JSString *str = ThisToStringForStringProto(cx, call);
         654          118340 :     if (!str)
         655               0 :         return false;
         656                 :
         657          118340 :     str = js_toLowerCase(cx, str);
         658          118340 :     if (!str)
         659               0 :         return false;
         660                 :
         661          118340 :     call.rval() = StringValue(str);
         662          118340 :     return true;
         663                 : }
         664                 :
         665                 : static JSBool
         666          118340 : str_toLowerCase(JSContext *cx, unsigned argc, Value *vp)
         667                 : {
         668          118340 :     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           22738 : js_toUpperCase(JSContext *cx, JSString *str)
         698                 : {
         699           22738 :     size_t n = str->length();
         700           22738 :     const jschar *s = str->getChars(cx);
         701           22738 :     if (!s)
         702               0 :         return NULL;
         703           22738 :     jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
         704           22738 :     if (!news)
         705               0 :         return NULL;
         706          110658 :     for (size_t i = 0; i < n; i++)
         707           87920 :         news[i] = unicode::ToUpperCase(s[i]);
         708           22738 :     news[n] = 0;
         709           22738 :     str = js_NewString(cx, news, n);
         710           22738 :     if (!str) {
         711               0 :         cx->free_(news);
         712               0 :         return NULL;
         713                 :     }
         714           22738 :     return str;
         715                 : }
         716                 :
         717                 : static JSBool
         718           22738 : ToUpperCaseHelper(JSContext *cx, CallReceiver call)
         719                 : {
         720           22738 :     JSString *str = ThisToStringForStringProto(cx, call);
         721           22738 :     if (!str)
         722               0 :         return false;
         723                 :
         724           22738 :     str = js_toUpperCase(cx, str);
         725           22738 :     if (!str)
         726               0 :         return false;
         727                 :
         728           22738 :     call.rval() = StringValue(str);
         729           22738 :     return true;
         730                 : }
         731                 :
         732                 : static JSBool
         733           22738 : str_toUpperCase(JSContext *cx, unsigned argc, Value *vp)
         734                 : {
         735           22738 :     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             148 : str_localeCompare(JSContext *cx, unsigned argc, Value *vp)
         765                 : {
         766             148 :     CallArgs args = CallArgsFromVp(argc, vp);
         767             148 :     JSString *str = ThisToStringForStringProto(cx, args);
         768             148 :     if (!str)
         769               0 :         return false;
         770                 :
         771             148 :     if (args.length() == 0) {
         772               9 :         args.rval() = Int32Value(0);
         773                 :     } else {
         774             139 :         JSString *thatStr = ToString(cx, args[0]);
         775             139 :         if (!thatStr)
         776               0 :             return false;
         777                 :
         778             139 :         if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
         779              85 :             args[0].setString(thatStr);
         780                 :
         781                 :             Value result;
         782              85 :             if (!cx->localeCallbacks->localeCompare(cx, str, thatStr, &result))
         783               0 :                 return true;
         784                 :
         785              85 :             args.rval() = result;
         786              85 :             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           61236 : js_str_charAt(JSContext *cx, unsigned argc, Value *vp)
         800                 : {
         801           61236 :     CallArgs args = CallArgsFromVp(argc, vp);
         802                 :
         803                 :     JSString *str;
         804                 :     size_t i;
         805           61236 :     if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
         806           61055 :         str = args.thisv().toString();
         807           61055 :         i = size_t(args[0].toInt32());
         808           61055 :         if (i >= str->length())
         809            3969 :             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           57150 :     str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, i);
         825           57150 :     if (!str)
         826               0 :         return false;
         827           57150 :     args.rval() = StringValue(str);
         828           57150 :     return true;
         829                 :
         830                 :   out_of_range:
         831            4086 :     args.rval() = StringValue(cx->runtime->emptyString);
         832            4086 :     return true;
         833                 : }
         834                 :
         835                 : JSBool
         836         4135224 : js_str_charCodeAt(JSContext *cx, unsigned argc, Value *vp)
         837                 : {
         838         4135224 :     CallArgs args = CallArgsFromVp(argc, vp);
         839                 :
         840                 :     JSString *str;
         841                 :     size_t i;
         842         4135224 :     if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
         843         3666752 :         str = args.thisv().toString();
         844         3666752 :         i = size_t(args[0].toInt32());
         845         3666752 :         if (i >= str->length())
         846             135 :             goto out_of_range;
         847                 :     } else {
         848          468472 :         str = ThisToStringForStringProto(cx, args);
         849          468472 :         if (!str)
         850               0 :             return false;
         851                 :
         852          468472 :         double d = 0.0;
         853          468472 :         if (args.length() > 0 && !ToInteger(cx, args[0], &d))
         854               0 :             return false;
         855                 :
         856          468472 :         if (d < 0 || str->length() <= d)
         857             306 :             goto out_of_range;
         858          468166 :         i = size_t(d);
         859                 :     }
         860                 :
         861                 :     const jschar *chars;
         862         4134783 :     chars = str->getChars(cx);
         863         4134783 :     if (!chars)
         864               0 :         return false;
         865                 :
         866         4134783 :     args.rval() = Int32Value(chars[i]);
         867         4134783 :     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              10 : js_BoyerMooreHorspool(const jschar *text, uint32_t textlen,
         886                 :                       const jschar *pat, uint32_t patlen)
         887                 : {
         888                 :     uint8_t skip[sBMHCharSetSize];
         889                 :
         890              10 :     JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
         891            2570 :     for (uint32_t i = 0; i < sBMHCharSetSize; i++)
         892            2560 :         skip[i] = (uint8_t)patlen;
         893              10 :     uint32_t m = patlen - 1;
         894             266 :     for (uint32_t i = 0; i < m; i++) {
         895             256 :         jschar c = pat[i];
         896             256 :         if (c >= sBMHCharSetSize)
         897               0 :             return sBMHBadPattern;
         898             256 :         skip[c] = (uint8_t)(m - i);
         899                 :     }
         900                 :     jschar c;
         901           12544 :     for (uint32_t k = m;
         902                 :          k < textlen;
         903           12534 :          k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
         904            6547 :         for (uint32_t i = k, j = m; ; i--, j--) {
         905            6547 :             if (text[i] != pat[j])
         906                 :                 break;
         907             280 :             if (j == 0)
         908               6 :                 return static_cast<int>(i);  /* safe: max string size */
         909                 :         }
         910                 :     }
         911               4 :     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           32016 :     static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, uint32_t patlen) {
         927           32016 :         return pat + patlen;
         928                 :     }
         929           36021 :     static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
         930          206606 :         for (; p != extent; ++p, ++t) {
         931          191132 :             if (*p != *t)
         932           20547 :                 return false;
         933                 :         }
         934           15474 :         return true;
         935                 :     }
         936                 : };
         937                 :
         938                 : template <class InnerMatch>
         939                 : static int
         940           32016 : UnrolledMatch(const jschar *text, uint32_t textlen, const jschar *pat, uint32_t patlen)
         941                 : {
         942           32016 :     JS_ASSERT(patlen > 0 && textlen > 0);
         943           32016 :     const jschar *textend = text + textlen - (patlen - 1);
         944           32016 :     const jschar p0 = *pat;
         945           32016 :     const jschar *const patNext = pat + 1;
         946           32016 :     const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
         947                 :     uint8_t fixup;
         948                 :
         949           32016 :     const jschar *t = text;
         950           32016 :     switch ((textend - t) & 7) {
         951            1701 :       case 0: if (*t++ == p0) { fixup = 8; goto match; }
         952            2830 :       case 7: if (*t++ == p0) { fixup = 7; goto match; }
         953            3736 :       case 6: if (*t++ == p0) { fixup = 6; goto match; }
         954            7404 :       case 5: if (*t++ == p0) { fixup = 5; goto match; }
         955            5484 :       case 4: if (*t++ == p0) { fixup = 4; goto match; }
         956           10168 :       case 3: if (*t++ == p0) { fixup = 3; goto match; }
         957           15908 :       case 2: if (*t++ == p0) { fixup = 2; goto match; }
         958           16411 :       case 1: if (*t++ == p0) { fixup = 1; goto match; }
         959                 :     }
         960          119103 :     while (t != textend) {
         961           90975 :       if (t[0] == p0) { t += 1; fixup = 8; goto match; }
         962           90868 :       if (t[1] == p0) { t += 2; fixup = 7; goto match; }
         963           87099 :       if (t[2] == p0) { t += 3; fixup = 6; goto match; }
         964           83413 :       if (t[3] == p0) { t += 4; fixup = 5; goto match; }
         965           80025 :       if (t[4] == p0) { t += 5; fixup = 4; goto match; }
         966           79898 :       if (t[5] == p0) { t += 6; fixup = 3; goto match; }
         967           79814 :       if (t[6] == p0) { t += 7; fixup = 2; goto match; }
         968           79543 :       if (t[7] == p0) { t += 8; fixup = 1; goto match; }
         969           79296 :         t += 8;
         970           79296 :         continue;
         971           65893 :         do {
         972           49258 :             if (*t++ == p0) {
         973                 :               match:
         974           36021 :                 if (!InnerMatch::match(patNext, t, extent))
         975           20547 :                     goto failed_match;
         976           15474 :                 return t - text - 1;
         977                 :             }
         978                 :           failed_match:;
         979                 :         } while (--fixup > 0);
         980                 :     }
         981           16542 :     return -1;
         982                 : }
         983                 :
         984                 : static JS_ALWAYS_INLINE int
         985        18371837 : StringMatch(const jschar *text, uint32_t textlen,
         986                 :             const jschar *pat, uint32_t patlen)
         987                 : {
         988        18371837 :     if (patlen == 0)
         989          201162 :         return 0;
         990        18170675 :     if (textlen < patlen)
         991           34808 :         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        18135867 :     if (patlen == 1) {
         999        18103841 :         const jschar p0 = *pat;
        1000      1145702993 :         for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
        1001      1128202427 :             if (*c == p0)
        1002          603275 :                 return c - text;
        1003                 :         }
        1004        17500566 :         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           32026 :     if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
        1022              10 :         int index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
        1023              10 :         if (index != sBMHBadPattern)
        1024              10 :             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           32016 :                           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          456558 : RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, uint32_t patlen, int *match)
        1050                 : {
        1051          456558 :     JS_ASSERT(textstr->isRope());
        1052                 :
        1053          456558 :     if (patlen == 0) {
        1054               0 :         *match = 0;
        1055               0 :         return true;
        1056                 :     }
        1057          456558 :     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          913116 :     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          456558 :         size_t textstrlen = textstr->length();
        1077          456558 :         size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
        1078          913116 :         StringSegmentRange r(cx);
        1079          456558 :         if (!r.init(textstr))
        1080               0 :             return false;
        1081        37205463 :         while (!r.empty()) {
        1082        36315546 :             if (threshold-- == 0 || !strs.append(r.front())) {
        1083           23199 :                 const jschar *chars = textstr->getChars(cx);
        1084           23199 :                 if (!chars)
        1085               0 :                     return false;
        1086           23199 :                 *match = StringMatch(chars, textstrlen, pat, patlen);
        1087           23199 :                 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           92665 : str_indexOf(JSContext *cx, unsigned argc, Value *vp)
        1150                 : {
        1151           92665 :     CallArgs args = CallArgsFromVp(argc, vp);
        1152           92665 :     JSString *str = ThisToStringForStringProto(cx, args);
        1153           92665 :     if (!str)
        1154               0 :         return false;
        1155                 :
        1156           92665 :     JSLinearString *patstr = ArgToRootedString(cx, args, 0);
        1157           92665 :     if (!patstr)
        1158               0 :         return false;
        1159                 :
        1160           92665 :     uint32_t textlen = str->length();
        1161           92665 :     const jschar *text = str->getChars(cx);
        1162           92665 :     if (!text)
        1163               0 :         return false;
        1164                 :
        1165           92665 :     uint32_t patlen = patstr->length();
        1166           92665 :     const jschar *pat = patstr->chars();
        1167                 :
        1168                 :     uint32_t start;
        1169           92665 :     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           92665 :         start = 0;
        1199                 :     }
        1200                 :
        1201           92665 :     int match = StringMatch(text, textlen, pat, patlen);
        1202           92665 :     args.rval() = Int32Value((match == -1) ? -1 : start + match);
        1203           92665 :     return true;
        1204                 : }
        1205                 :
        1206                 : static JSBool
        1207            2411 : str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
        1208                 : {
        1209            2411 :     CallArgs args = CallArgsFromVp(argc, vp);
        1210            2411 :     JSString *textstr = ThisToStringForStringProto(cx, args);
        1211            2411 :     if (!textstr)
        1212               0 :         return false;
        1213                 :
        1214            2411 :     size_t textlen = textstr->length();
        1215            2411 :     const jschar *text = textstr->getChars(cx);
        1216            2411 :     if (!text)
        1217               0 :         return false;
        1218                 :
        1219            2411 :     JSLinearString *patstr = ArgToRootedString(cx, args, 0);
        1220            2411 :     if (!patstr)
        1221               0 :         return false;
        1222                 :
        1223            2411 :     size_t patlen = patstr->length();
        1224            2411 :     const jschar *pat = patstr->chars();
        1225                 :
        1226            2411 :     int i = textlen - patlen; // Start searching here
        1227            2411 :     if (i < 0) {
        1228               0 :         args.rval() = Int32Value(-1);
        1229               0 :         return true;
        1230                 :     }
        1231                 :
        1232            2411 :     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            2411 :     if (patlen == 0) {
        1254               0 :         args.rval() = Int32Value(i);
        1255               0 :         return true;
        1256                 :     }
        1257                 :
        1258            2411 :     const jschar *t = text + i;
        1259            2411 :     const jschar *textend = text - 1;
        1260            2411 :     const jschar p0 = *pat;
        1261            2411 :     const jschar *patNext = pat + 1;
        1262            2411 :     const jschar *patEnd = pat + patlen;
        1263                 :
        1264           22963 :     for (; t != textend; --t) {
        1265           22421 :         if (*t == p0) {
        1266            1869 :             const jschar *t1 = t + 1;
        1267            1869 :             for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
        1268               0 :                 if (*t1 != *p1)
        1269               0 :                     goto break_continue;
        1270                 :             }
        1271            1869 :             args.rval() = Int32Value(t - text);
        1272            1869 :             return true;
        1273                 :         }
        1274                 :       break_continue:;
        1275                 :     }
        1276                 :
        1277             542 :     args.rval() = Int32Value(-1);
        1278             542 :     return true;
        1279                 : }
        1280                 :
        1281                 : static JSBool
        1282            8418 : js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
        1283                 : {
        1284            8418 :     CallReceiver call = CallReceiverFromVp(vp);
        1285            8418 :     JSString *str = ThisToStringForStringProto(cx, call);
        1286            8418 :     if (!str)
        1287               0 :         return false;
        1288            8418 :     size_t length = str->length();
        1289            8418 :     const jschar *chars = str->getChars(cx);
        1290            8418 :     if (!chars)
        1291               0 :         return false;
        1292                 :
        1293            8418 :     size_t begin = 0;
        1294            8418 :     size_t end = length;
        1295                 :
        1296            8418 :     if (trimLeft) {
        1297           21307 :         while (begin < length && unicode::IsSpace(chars[begin]))
        1298            4507 :             ++begin;
        1299                 :     }
        1300                 :
        1301            8418 :     if (trimRight) {
        1302           18096 :         while (end > begin && unicode::IsSpace(chars[end - 1]))
        1303            1260 :             --end;
        1304                 :     }
        1305                 :
        1306            8418 :     str = js_NewDependentString(cx, str, begin, end - begin);
        1307            8418 :     if (!str)
        1308               0 :         return false;
        1309                 :
        1310            8418 :     call.rval() = StringValue(str);
        1311            8418 :     return true;
        1312                 : }
        1313                 :
        1314                 : static JSBool
        1315            8400 : str_trim(JSContext *cx, unsigned argc, Value *vp)
        1316                 : {
        1317            8400 :     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         1095161 :     FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
        1348            3960 :     JSLinearString *pattern() const { return patstr; }
        1349           40158 :     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          542145 :     int32_t match() const { return match_; }
        1356                 : };
        1357                 :
        1358                 : static inline bool
        1359          825479 : IsRegExpMetaChar(jschar c)
        1360                 : {
        1361          825479 :     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           21932 :         return true;
        1367                 :       default:
        1368          803547 :         return false;
        1369                 :     }
        1370                 : }
        1371                 :
        1372                 : static inline bool
        1373          510941 : HasRegExpMetaChars(const jschar *chars, size_t length)
        1374                 : {
        1375         1310597 :     for (size_t i = 0; i < length; ++i) {
        1376          819503 :         if (IsRegExpMetaChar(chars[i]))
        1377           19847 :             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         1095161 : {
        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            3872 :     flattenPattern(JSContext *cx, JSAtom *patstr)
        1404                 :     {
        1405            7744 :         StringBuffer sb(cx);
        1406            3872 :         if (!sb.reserve(patstr->length()))
        1407               0 :             return NULL;
        1408                 :
        1409                 :         static const jschar ESCAPE_CHAR = '\\';
        1410            3872 :         const jschar *chars = patstr->chars();
        1411            3872 :         size_t len = patstr->length();
        1412            9848 :         for (const jschar *it = chars; it != chars + len; ++it) {
        1413            5976 :             if (IsRegExpMetaChar(*it)) {
        1414            2085 :                 if (!sb.append(ESCAPE_CHAR) || !sb.append(*it))
        1415               0 :                     return NULL;
        1416                 :             } else {
        1417            3891 :                 if (!sb.append(*it))
        1418               0 :                     return NULL;
        1419                 :             }
        1420                 :         }
        1421            3872 :         return sb.finishAtom();
        1422                 :     }
        1423                 :
        1424                 :   public:
        1425         1095161 :     StringRegExpGuard() {}
        1426                 :
        1427                 :     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
        1428         1095161 :     bool init(JSContext *cx, CallArgs args, bool convertVoid = false)
        1429                 :     {
        1430         1095161 :         if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
        1431          523561 :             if (!RegExpToShared(cx, args[0].toObject(), &re_))
        1432               0 :                 return false;
        1433                 :         } else {
        1434          571600 :             if (convertVoid && !args.hasDefined(0)) {
        1435           11556 :                 fm.patstr = cx->runtime->emptyString;
        1436           11556 :                 return true;
        1437                 :             }
        1438                 :
        1439          560044 :             JSString *arg = ArgToRootedString(cx, args, 0);
        1440          560044 :             if (!arg)
        1441              54 :                 return false;
        1442                 :
        1443          559990 :             fm.patstr = js_AtomizeString(cx, arg);
        1444          559990 :             if (!fm.patstr)
        1445               0 :                 return false;
        1446                 :         }
        1447         1083551 :         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         1095107 :     tryFlatMatch(JSContext *cx, JSString *textstr, unsigned optarg, unsigned argc,
        1462                 :                  bool checkMetaChars = true)
        1463                 :     {
        1464         1095107 :         if (re_.initialized())
        1465          523561 :             return NULL;
        1466                 :
        1467          571546 :         fm.pat = fm.patstr->chars();
        1468          571546 :         fm.patlen = fm.patstr->length();
        1469                 :
        1470          571546 :         if (optarg < argc)
        1471           26912 :             return NULL;
        1472                 :
        1473         1055575 :         if (checkMetaChars &&
        1474          510941 :             (fm.patlen > MAX_FLAT_PAT_LEN || HasRegExpMetaChars(fm.pat, fm.patlen))) {
        1475           19847 :             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          524787 :         if (textstr->isRope()) {
        1483          456558 :             if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_))
        1484               0 :                 return NULL;
        1485                 :         } else {
        1486           68229 :             const jschar *text = textstr->asLinear().chars();
        1487           68229 :             size_t textlen = textstr->length();
        1488           68229 :             fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
        1489                 :         }
        1490          524787 :         return &fm;
        1491                 :     }
        1492                 :
        1493                 :     /* If the pattern is not already a regular expression, make it so. */
        1494          570320 :     bool normalizeRegExp(JSContext *cx, bool flat, unsigned optarg, CallArgs args)
        1495                 :     {
        1496          570320 :         if (re_.initialized())
        1497          523561 :             return true;
        1498                 :
        1499                 :         /* Build RegExp from pattern string. */
        1500                 :         JSString *opt;
        1501           46759 :         if (optarg < args.length()) {
        1502           26912 :             opt = ToString(cx, args[optarg]);
        1503           26912 :             if (!opt)
        1504               0 :                 return false;
        1505                 :         } else {
        1506           19847 :             opt = NULL;
        1507                 :         }
        1508                 :
        1509                 :         JSAtom *patstr;
        1510           46759 :         if (flat) {
        1511            3872 :             patstr = flattenPattern(cx, fm.patstr);
        1512            3872 :             if (!patstr)
        1513               0 :                 return false;
        1514                 :         } else {
        1515           42887 :             patstr = fm.patstr;
        1516                 :         }
        1517           46759 :         JS_ASSERT(patstr);
        1518                 :
        1519           46759 :         return cx->compartment->regExps.get(cx, patstr, opt, &re_);
        1520                 :     }
        1521                 :
        1522          610325 :     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         2268495 : Matched(RegExpExecType type, const Value &v)
        1528                 : {
        1529         2268495 :     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          550793 : DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re,
        1551                 :         DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
        1552                 : {
        1553          550793 :     JSLinearString *linearStr = str->ensureLinear(cx);
        1554          550793 :     if (!linearStr)
        1555               0 :         return false;
        1556                 :
        1557          550793 :     const jschar *chars = linearStr->chars();
        1558          550793 :     size_t length = linearStr->length();
        1559                 :
        1560          550793 :     if (re.global()) {
        1561          348900 :         RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
        1562         2135642 :         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
        1563         2096743 :             if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
        1564               0 :                 return false;
        1565         2096743 :             if (!Matched(type, *rval))
        1566          310001 :                 break;
        1567         1786742 :             if (!callback(cx, res, count, data))
        1568               0 :                 return false;
        1569         1786742 :             if (!res->matched())
        1570           77879 :                 ++i;
        1571                 :         }
        1572                 :     } else {
        1573          201893 :         RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
        1574          201893 :         bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
        1575          201893 :         size_t i = 0;
        1576          201893 :         if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
        1577               0 :             return false;
        1578          201893 :         if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
        1579               9 :             return false;
        1580                 :     }
        1581          550784 :     return true;
        1582                 : }
        1583                 :
        1584                 : static bool
        1585           25920 : BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, CallArgs *args)
        1586                 : {
        1587           25920 :     if (fm.match() < 0) {
        1588           21960 :         args->rval() = NullValue();
        1589           21960 :         return true;
        1590                 :     }
        1591                 :
        1592                 :     /* For this non-global match, produce a RegExp.exec-style array. */
        1593            3960 :     JSObject *obj = NewSlowEmptyArray(cx);
        1594            3960 :     if (!obj)
        1595               0 :         return false;
        1596                 :
        1597           11880 :     if (!obj->defineElement(cx, 0, StringValue(fm.pattern())) ||
        1598            3960 :         !obj->defineProperty(cx, cx->runtime->atomState.indexAtom, Int32Value(fm.match())) ||
        1599            3960 :         !obj->defineProperty(cx, cx->runtime->atomState.inputAtom, StringValue(textstr)))
        1600                 :     {
        1601               0 :         return false;
        1602                 :     }
        1603                 :
        1604            3960 :     args->rval() = ObjectValue(*obj);
        1605            3960 :     return true;
        1606                 : }
        1607                 :
        1608                 : typedef JSObject **MatchArgType;
        1609                 :
        1610                 : /*
        1611                 :  * DoMatch will only callback on global matches, hence this function builds
        1612                 :  * only the "array of matches" returned by match on global regexps.
        1613                 :  */
        1614                 : static bool
        1615            2187 : MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
        1616                 : {
        1617            2187 :     JS_ASSERT(count <= JSID_INT_MAX);  /* by max string length */
        1618                 :
        1619            2187 :     JSObject *&arrayobj = *static_cast<MatchArgType>(p);
        1620            2187 :     if (!arrayobj) {
        1621             405 :         arrayobj = NewDenseEmptyArray(cx);
        1622             405 :         if (!arrayobj)
        1623               0 :             return false;
        1624                 :     }
        1625                 :
        1626                 :     Value v;
        1627            2187 :     return res->createLastMatch(cx, &v) && arrayobj->defineElement(cx, count, v);
        1628                 : }
        1629                 :
        1630                 : JSBool
        1631           65943 : js::str_match(JSContext *cx, unsigned argc, Value *vp)
        1632                 : {
        1633           65943 :     CallArgs args = CallArgsFromVp(argc, vp);
        1634           65943 :     JSString *str = ThisToStringForStringProto(cx, args);
        1635           65943 :     if (!str)
        1636               0 :         return false;
        1637                 :
        1638          131886 :     StringRegExpGuard g;
        1639           65943 :     if (!g.init(cx, args, true))
        1640              18 :         return false;
        1641                 :
        1642           65925 :     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length()))
        1643           25920 :         return BuildFlatMatchArray(cx, str, *fm, &args);
        1644                 :
        1645                 :     /* Return if there was an error in tryFlatMatch. */
        1646           40005 :     if (cx->isExceptionPending())
        1647               0 :         return false;
        1648                 :
        1649           40005 :     if (!g.normalizeRegExp(cx, false, 1, args))
        1650               0 :         return false;
        1651                 :
        1652           40005 :     JSObject *array = NULL;
        1653           40005 :     MatchArgType arg = &array;
        1654           40005 :     RegExpStatics *res = cx->regExpStatics();
        1655                 :     Value rval;
        1656           40005 :     if (!DoMatch(cx, res, str, g.regExp(), MatchCallback, arg, MATCH_ARGS, &rval))
        1657               0 :         return false;
        1658                 :
        1659           40005 :     if (g.regExp().global())
        1660            9864 :         args.rval() = ObjectOrNullValue(array);
        1661                 :     else
        1662           30141 :         args.rval() = rval;
        1663           40005 :     return true;
        1664                 : }
        1665                 :
        1666                 : JSBool
        1667          484719 : js::str_search(JSContext *cx, unsigned argc, Value *vp)
        1668                 : {
        1669          484719 :     CallArgs args = CallArgsFromVp(argc, vp);
        1670          484719 :     JSString *str = ThisToStringForStringProto(cx, args);
        1671          484719 :     if (!str)
        1672               0 :         return false;
        1673                 :
        1674          969438 :     StringRegExpGuard g;
        1675          484719 :     if (!g.init(cx, args, true))
        1676              18 :         return false;
        1677          484701 :     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) {
        1678          465174 :         args.rval() = Int32Value(fm->match());
        1679          465174 :         return true;
        1680                 :     }
        1681                 :
        1682           19527 :     if (cx->isExceptionPending())  /* from tryFlatMatch */
        1683               0 :         return false;
        1684                 :
        1685           19527 :     if (!g.normalizeRegExp(cx, false, 1, args))
        1686               0 :         return false;
        1687                 :
        1688           19527 :     JSLinearString *linearStr = str->ensureLinear(cx);
        1689           19527 :     if (!linearStr)
        1690               0 :         return false;
        1691                 :
        1692           19527 :     const jschar *chars = linearStr->chars();
        1693           19527 :     size_t length = linearStr->length();
        1694           19527 :     RegExpStatics *res = cx->regExpStatics();
        1695                 :
        1696                 :     /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
        1697           19527 :     size_t i = 0;
        1698                 :     Value result;
        1699           19527 :     if (!ExecuteRegExp(cx, res, g.regExp(), linearStr, chars, length, &i, RegExpTest, &result))
        1700               0 :         return false;
        1701                 :
        1702           19527 :     if (result.isTrue())
        1703              76 :         args.rval() = Int32Value(res->matchStart());
        1704                 :     else
        1705           19451 :         args.rval() = Int32Value(-1);
        1706           19527 :     return true;
        1707                 : }
        1708                 :
        1709                 : struct ReplaceData
        1710          544499 : {
        1711          544499 :     ReplaceData(JSContext *cx)
        1712          544499 :      : sb(cx)
        1713          544499 :     {}
        1714                 :
        1715                 :     JSString           *str;           /* 'this' parameter object as a string */
        1716                 :     StringRegExpGuard  g;              /* regexp parameter object and private data */
        1717                 :     JSObject           *lambda;        /* replacement function object or null */
        1718                 :     JSObject           *elembase;      /* object for function(a){return b[a]} replace */
        1719                 :     JSLinearString     *repstr;        /* replacement string */
        1720                 :     const jschar       *dollar;        /* null or pointer to first $ in repstr */
        1721                 :     const jschar       *dollarEnd;     /* limit pointer for js_strchr_limit */
        1722                 :     int                leftIndex;      /* left context index in str->chars */
        1723                 :     JSSubString        dollarStr;      /* for "$$" InterpretDollar result */
        1724                 :     bool               calledBack;     /* record whether callback has been called */
        1725                 :     InvokeArgsGuard    args;           /* arguments for lambda call */
        1726                 :     StringBuffer       sb;             /* buffer built during DoMatch */
        1727                 : };
        1728                 :
        1729                 : static bool
        1730            8984 : InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep,
        1731                 :                 ReplaceData &rdata, JSSubString *out, size_t *skip)
        1732                 : {
        1733            8984 :     JS_ASSERT(*dp == '$');
        1734                 :
        1735                 :     /* If there is only a dollar, bail now */
        1736            8984 :     if (dp + 1 >= ep)
        1737               0 :         return false;
        1738                 :
        1739                 :     /* Interpret all Perl match-induced dollar variables. */
        1740            8984 :     jschar dc = dp[1];
        1741            8984 :     if (JS7_ISDEC(dc)) {
        1742                 :         /* ECMA-262 Edition 3: 1-9 or 01-99 */
        1743            8966 :         unsigned num = JS7_UNDEC(dc);
        1744            8966 :         if (num > res->parenCount())
        1745               0 :             return false;
        1746                 :
        1747            8966 :         const jschar *cp = dp + 2;
        1748            8966 :         if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
        1749               0 :             unsigned tmp = 10 * num + JS7_UNDEC(dc);
        1750               0 :             if (tmp <= res->parenCount()) {
        1751               0 :                 cp++;
        1752               0 :                 num = tmp;
        1753                 :             }
        1754                 :         }
        1755            8966 :         if (num == 0)
        1756               0 :             return false;
        1757                 :
        1758            8966 :         *skip = cp - dp;
        1759                 :
        1760            8966 :         JS_ASSERT(num <= res->parenCount());
        1761                 :
        1762                 :         /*
        1763                 :          * Note: we index to get the paren with the (1-indexed) pair
        1764                 :          * number, as opposed to a (0-indexed) paren number.
        1765                 :          */
        1766            8966 :         res->getParen(num, out);
        1767            8966 :         return true;
        1768                 :     }
        1769                 :
        1770              18 :     *skip = 2;
        1771              18 :     switch (dc) {
        1772                 :       case '$':
        1773               0 :         rdata.dollarStr.chars = dp;
        1774               0 :         rdata.dollarStr.length = 1;
        1775               0 :         *out = rdata.dollarStr;
        1776               0 :         return true;
        1777                 :       case '&':
        1778              18 :         res->getLastMatch(out);
        1779              18 :         return true;
        1780                 :       case '+':
        1781               0 :         res->getLastParen(out);
        1782               0 :         return true;
        1783                 :       case '`':
        1784               0 :         res->getLeftContext(out);
        1785               0 :         return true;
        1786                 :       case '\'':
        1787               0 :         res->getRightContext(out);
        1788               0 :         return true;
        1789                 :     }
        1790               0 :     return false;
        1791                 : }
        1792                 :
        1793                 : static bool
        1794         1870144 : FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
        1795                 : {
        1796         1870144 :     JSObject *base = rdata.elembase;
        1797         1870144 :     if (base) {
        1798                 :         /*
        1799                 :          * The base object is used when replace was passed a lambda which looks like
        1800                 :          * 'function(a) { return b[a]; }' for the base object b.  b will not change
        1801                 :          * in the course of the replace unless we end up making a scripted call due
        1802                 :          * to accessing a scripted getter or a value with a scripted toString.
        1803                 :          */
        1804          733248 :         JS_ASSERT(rdata.lambda);
        1805          733248 :         JS_ASSERT(!base->getOps()->lookupProperty);
        1806          733248 :         JS_ASSERT(!base->getOps()->getProperty);
        1807                 :
        1808                 :         Value match;
        1809          733248 :         if (!res->createLastMatch(cx, &match))
        1810               0 :             return false;
        1811          733248 :         JSString *str = match.toString();
        1812                 :
        1813                 :         JSAtom *atom;
        1814          733248 :         if (str->isAtom()) {
        1815          733248 :             atom = &str->asAtom();
        1816                 :         } else {
        1817               0 :             atom = js_AtomizeString(cx, str);
        1818               0 :             if (!atom)
        1819               0 :                 return false;
        1820                 :         }
        1821          733248 :         jsid id = ATOM_TO_JSID(atom);
        1822                 :
        1823                 :         JSObject *holder;
        1824          733248 :         JSProperty *prop = NULL;
        1825          733248 :         if (!LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop))
        1826               0 :             return false;
        1827                 :
        1828                 :         /* Only handle the case where the property exists and is on this object. */
        1829          733248 :         if (prop && holder == base) {
        1830          733230 :             Shape *shape = (Shape *) prop;
        1831          733230 :             if (shape->hasSlot() && shape->hasDefaultGetter()) {
        1832          733230 :                 Value value = base->getSlot(shape->slot());
        1833          733230 :                 if (value.isString()) {
        1834          733230 :                     rdata.repstr = value.toString()->ensureLinear(cx);
        1835          733230 :                     if (!rdata.repstr)
        1836               0 :                         return false;
        1837          733230 :                     *sizep = rdata.repstr->length();
        1838          733230 :                     return true;
        1839                 :                 }
        1840                 :             }
        1841                 :         }
        1842                 :
        1843                 :         /*
        1844                 :          * Couldn't handle this property, fall through and despecialize to the
        1845                 :          * general lambda case.
        1846                 :          */
        1847              18 :         rdata.elembase = NULL;
        1848                 :     }
        1849                 :
        1850         1136914 :     JSObject *lambda = rdata.lambda;
        1851         1136914 :     if (lambda) {
        1852           56722 :         PreserveRegExpStatics staticsGuard(res);
        1853           28361 :         if (!staticsGuard.init(cx))
        1854               0 :             return false;
        1855                 :
        1856                 :         /*
        1857                 :          * In the lambda case, not only do we find the replacement string's
        1858                 :          * length, we compute repstr and return it via rdata for use within
        1859                 :          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
        1860                 :          * index, input), i.e., all the properties of a regexp match array.
        1861                 :          * For $&, etc., we must create string jsvals from cx->regExpStatics.
        1862                 :          * We grab up stack space to keep the newborn strings GC-rooted.
        1863                 :          */
        1864           28361 :         unsigned p = res->parenCount();
        1865           28361 :         unsigned argc = 1 + p + 2;
        1866                 :
        1867           28361 :         InvokeArgsGuard &args = rdata.args;
        1868           28361 :         if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args))
        1869               0 :             return false;
        1870                 :
        1871           28361 :         args.setCallee(ObjectValue(*lambda));
        1872           28361 :         args.thisv() = UndefinedValue();
        1873                 :
        1874                 :         /* Push $&, $1, $2, ... */
        1875           28361 :         unsigned argi = 0;
        1876           28361 :         if (!res->createLastMatch(cx, &args[argi++]))
        1877               0 :             return false;
        1878                 :
        1879           51363 :         for (size_t i = 0; i < res->parenCount(); ++i) {
        1880           23002 :             if (!res->createParen(cx, i + 1, &args[argi++]))
        1881               0 :                 return false;
        1882                 :         }
        1883                 :
        1884                 :         /* Push match index and input string. */
        1885           28361 :         args[argi++].setInt32(res->matchStart());
        1886           28361 :         args[argi].setString(rdata.str);
        1887                 :
        1888           28361 :         if (!Invoke(cx, args))
        1889               9 :             return false;
        1890                 :
        1891                 :         /* root repstr: rdata is on the stack, so scanned by conservative gc. */
        1892           28352 :         JSString *repstr = ToString(cx, args.rval());
        1893           28352 :         if (!repstr)
        1894               0 :             return false;
        1895           28352 :         rdata.repstr = repstr->ensureLinear(cx);
        1896           28352 :         if (!rdata.repstr)
        1897               0 :             return false;
        1898           28352 :         *sizep = rdata.repstr->length();
        1899           28352 :         return true;
        1900                 :     }
        1901                 :
        1902         1108553 :     JSString *repstr = rdata.repstr;
        1903         1108553 :     size_t replen = repstr->length();
        1904         1113045 :     for (const jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp;
        1905                 :          dp = js_strchr_limit(dp, '$', ep)) {
        1906                 :         JSSubString sub;
        1907                 :         size_t skip;
        1908            4492 :         if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
        1909            4492 :             replen += sub.length - skip;
        1910            4492 :             dp += skip;
        1911                 :         } else {
        1912               0 :             dp++;
        1913                 :         }
        1914                 :     }
        1915         1108553 :     *sizep = replen;
        1916         1108553 :     return true;
        1917                 : }
        1918                 :
        1919                 : /*
        1920                 :  * Precondition: |rdata.sb| already has necessary growth space reserved (as
        1921                 :  * derived from FindReplaceLength).
        1922                 :  */
        1923                 : static void
        1924         1870135 : DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
        1925                 : {
        1926         1870135 :     JSLinearString *repstr = rdata.repstr;
        1927                 :     const jschar *cp;
        1928         1870135 :     const jschar *bp = cp = repstr->chars();
        1929                 :
        1930         1870135 :     const jschar *dp = rdata.dollar;
        1931         1870135 :     const jschar *ep = rdata.dollarEnd;
        1932         1874627 :     for (; dp; dp = js_strchr_limit(dp, '$', ep)) {
        1933                 :         /* Move one of the constant portions of the replacement value. */
        1934            4492 :         size_t len = dp - cp;
        1935            4492 :         rdata.sb.infallibleAppend(cp, len);
        1936            4492 :         cp = dp;
        1937                 :
        1938                 :         JSSubString sub;
        1939                 :         size_t skip;
        1940            4492 :         if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
        1941            4492 :             len = sub.length;
        1942            4492 :             rdata.sb.infallibleAppend(sub.chars, len);
        1943            4492 :             cp += skip;
        1944            4492 :             dp += skip;
        1945                 :         } else {
        1946               0 :             dp++;
        1947                 :         }
        1948                 :     }
        1949         1870135 :     rdata.sb.infallibleAppend(cp, repstr->length() - (cp - bp));
        1950         1870135 : }
        1951                 :
        1952                 : static bool
        1953         1870144 : ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
        1954                 : {
        1955         1870144 :     ReplaceData &rdata = *static_cast<ReplaceData *>(p);
        1956                 :
        1957         1870144 :     rdata.calledBack = true;
        1958         1870144 :     JSLinearString &str = rdata.str->asLinear();  /* flattened for regexp */
        1959         1870144 :     size_t leftoff = rdata.leftIndex;
        1960         1870144 :     const jschar *left = str.chars() + leftoff;
        1961         1870144 :     size_t leftlen = res->matchStart() - leftoff;
        1962         1870144 :     rdata.leftIndex = res->matchLimit();
        1963                 :
        1964         1870144 :     size_t replen = 0;  /* silence 'unused' warning */
        1965         1870144 :     if (!FindReplaceLength(cx, res, rdata, &replen))
        1966               9 :         return false;
        1967                 :
        1968         1870135 :     size_t growth = leftlen + replen;
        1969         1870135 :     if (!rdata.sb.reserve(rdata.sb.length() + growth))
        1970               0 :         return false;
        1971         1870135 :     rdata.sb.infallibleAppend(left, leftlen); /* skipped-over portion of the search value */
        1972         1870135 :     DoReplace(cx, res, rdata);
        1973         1870135 :     return true;
        1974                 : }
        1975                 :
        1976                 : static bool
        1977           13380 : BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
        1978                 :                      const FlatMatch &fm, CallArgs *args)
        1979                 : {
        1980           13380 :     RopeBuilder builder(cx);
        1981           13380 :     size_t match = fm.match();
        1982           13380 :     size_t matchEnd = match + fm.patternLength();
        1983                 :
        1984           13380 :     if (textstr->isRope()) {
        1985                 :         /*
        1986                 :          * If we are replacing over a rope, avoid flattening it by iterating
        1987                 :          * through it, building a new rope.
        1988                 :          */
        1989              18 :         StringSegmentRange r(cx);
        1990               9 :         if (!r.init(textstr))
        1991               0 :             return false;
        1992               9 :         size_t pos = 0;
        1993              36 :         while (!r.empty()) {
        1994              18 :             JSString *str = r.front();
        1995              18 :             size_t len = str->length();
        1996              18 :             size_t strEnd = pos + len;
        1997              18 :             if (pos < matchEnd && strEnd > match) {
        1998                 :                 /*
        1999                 :                  * We need to special-case any part of the rope that overlaps
        2000                 :                  * with the replacement string.
        2001                 :                  */
        2002               9 :                 if (match >= pos) {
        2003                 :                     /*
        2004                 :                      * If this part of the rope overlaps with the left side of
        2005                 :                      * the pattern, then it must be the only one to overlap with
        2006                 :                      * the first character in the pattern, so we include the
        2007                 :                      * replacement string here.
        2008                 :                      */
        2009               9 :                     JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos);
        2010              27 :                     if (!leftSide ||
        2011               9 :                         !builder.append(leftSide) ||
        2012               9 :                         !builder.append(repstr)) {
        2013               0 :                         return false;
        2014                 :                     }
        2015                 :                 }
        2016                 :
        2017                 :                 /*
        2018                 :                  * If str runs off the end of the matched string, append the
        2019                 :                  * last part of str.
        2020                 :                  */
        2021               9 :                 if (strEnd > matchEnd) {
        2022                 :                     JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos,
        2023               0 :                                                                 strEnd - matchEnd);
        2024               0 :                     if (!rightSide || !builder.append(rightSide))
        2025               0 :                         return false;
        2026               9 :                 }
        2027                 :             } else {
        2028               9 :                 if (!builder.append(str))
        2029               0 :                     return false;
        2030                 :             }
        2031              18 :             pos += str->length();
        2032              18 :             if (!r.popFront())
        2033               0 :                 return false;
        2034                 :         }
        2035                 :     } else {
        2036           13371 :         JSString *leftSide = js_NewDependentString(cx, textstr, 0, match);
        2037           13371 :         if (!leftSide)
        2038               0 :             return false;
        2039           13371 :         JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(),
        2040           26742 :                                                     textstr->length() - match - fm.patternLength());
        2041           53484 :         if (!rightSide ||
        2042           13371 :             !builder.append(leftSide) ||
        2043           13371 :             !builder.append(repstr) ||
        2044           13371 :             !builder.append(rightSide)) {
        2045               0 :             return false;
        2046                 :         }
        2047                 :     }
        2048                 :
        2049           13380 :     args->rval() = StringValue(builder.result());
        2050           13380 :     return true;
        2051                 : }
        2052                 :
        2053                 : /*
        2054                 :  * Perform a linear-scan dollar substitution on the replacement text,
        2055                 :  * constructing a result string that looks like:
        2056                 :  *
        2057                 :  *      newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
        2058                 :  */
        2059                 : static inline bool
        2060              18 : BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr,
        2061                 :                        const jschar *firstDollar, const FlatMatch &fm, CallArgs *args)
        2062                 : {
        2063              18 :     JSLinearString *textstr = textstrArg->ensureLinear(cx);
        2064              18 :     if (!textstr)
        2065               0 :         return NULL;
        2066                 :
        2067              18 :     JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
        2068              18 :     size_t matchStart = fm.match();
        2069              18 :     size_t matchLimit = matchStart + fm.patternLength();
        2070                 :
        2071                 :     /*
        2072                 :      * Most probably:
        2073                 :      *
        2074                 :      *      len(newstr) >= len(orig) - len(match) + len(replacement)
        2075                 :      *
        2076                 :      * Note that dollar vars _could_ make the resulting text smaller than this.
        2077                 :      */
        2078              36 :     StringBuffer newReplaceChars(cx);
        2079              18 :     if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
        2080               0 :         return false;
        2081                 :
        2082                 :     /* Move the pre-dollar chunk in bulk. */
        2083              18 :     newReplaceChars.infallibleAppend(repstr->chars(), firstDollar);
        2084                 :
        2085                 :     /* Move the rest char-by-char, interpreting dollars as we encounter them. */
        2086                 : #define ENSURE(__cond) if (!(__cond)) return false;
        2087              18 :     const jschar *repstrLimit = repstr->chars() + repstr->length();
        2088              63 :     for (const jschar *it = firstDollar; it < repstrLimit; ++it) {
        2089              45 :         if (*it != '$' || it == repstrLimit - 1) {
        2090              27 :             ENSURE(newReplaceChars.append(*it));
        2091              27 :             continue;
        2092                 :         }
        2093                 :
        2094              18 :         switch (*(it + 1)) {
        2095                 :           case '$': /* Eat one of the dollars. */
        2096               0 :             ENSURE(newReplaceChars.append(*it));
        2097               0 :             break;
        2098                 :           case '&':
        2099              18 :             ENSURE(newReplaceChars.append(textstr->chars() + matchStart,
        2100                 :                                           textstr->chars() + matchLimit));
        2101              18 :             break;
        2102                 :           case '`':
        2103               0 :             ENSURE(newReplaceChars.append(textstr->chars(), textstr->chars() + matchStart));
        2104               0 :             break;
        2105                 :           case '\'':
        2106               0 :             ENSURE(newReplaceChars.append(textstr->chars() + matchLimit,
        2107                 :                                           textstr->chars() + textstr->length()));
        2108               0 :             break;
        2109                 :           default: /* The dollar we saw was not special (no matter what its mother told it). */
        2110               0 :             ENSURE(newReplaceChars.append(*it));
        2111               0 :             continue;
        2112                 :         }
        2113              18 :         ++it; /* We always eat an extra char in the above switch. */
        2114                 :     }
        2115                 :
        2116              18 :     JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart);
        2117              18 :     ENSURE(leftSide);
        2118                 :
        2119              18 :     JSString *newReplace = newReplaceChars.finishString();
        2120              18 :     ENSURE(newReplace);
        2121                 :
        2122              18 :     JS_ASSERT(textstr->length() >= matchLimit);
        2123                 :     JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit,
        2124              18 :                                                 textstr->length() - matchLimit);
        2125              18 :     ENSURE(rightSide);
        2126                 :
        2127              18 :     RopeBuilder builder(cx);
        2128              18 :     ENSURE(builder.append(leftSide) &&
        2129                 :            builder.append(newReplace) &&
        2130                 :            builder.append(rightSide));
        2131                 : #undef ENSURE
        2132                 :
        2133              18 :     args->rval() = StringValue(builder.result());
        2134              18 :     return true;
        2135                 : }
        2136                 :
        2137                 : static inline bool
        2138          510788 : str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata)
        2139                 : {
        2140          510788 :     if (!rdata.g.normalizeRegExp(cx, true, 2, args))
        2141               0 :         return false;
        2142                 :
        2143          510788 :     rdata.leftIndex = 0;
        2144          510788 :     rdata.calledBack = false;
        2145                 :
        2146          510788 :     RegExpStatics *res = cx->regExpStatics();
        2147          510788 :     RegExpShared &re = rdata.g.regExp();
        2148                 :
        2149                 :     Value tmp;
        2150          510788 :     if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
        2151               9 :         return false;
        2152                 :
        2153          510779 :     if (!rdata.calledBack) {
        2154                 :         /* Didn't match, so the string is unmodified. */
        2155          300804 :         args.rval() = StringValue(rdata.str);
        2156          300804 :         return true;
        2157                 :     }
        2158                 :
        2159                 :     JSSubString sub;
        2160          209975 :     res->getRightContext(&sub);
        2161          209975 :     if (!rdata.sb.append(sub.chars, sub.length))
        2162               0 :         return false;
        2163                 :
        2164          209975 :     JSString *retstr = rdata.sb.finishString();
        2165          209975 :     if (!retstr)
        2166               0 :         return false;
        2167                 :
        2168          209975 :     args.rval() = StringValue(retstr);
        2169          209975 :     return true;
        2170                 : }
        2171                 :
        2172                 : static inline bool
        2173               0 : str_replace_flat_lambda(JSContext *cx, CallArgs outerArgs, ReplaceData &rdata, const FlatMatch &fm)
        2174                 : {
        2175               0 :     JS_ASSERT(fm.match() >= 0);
        2176                 :
        2177               0 :     JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength());
        2178               0 :     if (!matchStr)
        2179               0 :         return false;
        2180                 :
        2181                 :     /* lambda(matchStr, matchStart, textstr) */
        2182                 :     static const uint32_t lambdaArgc = 3;
        2183               0 :     if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.args))
        2184               0 :         return false;
        2185                 :
        2186               0 :     CallArgs &args = rdata.args;
        2187               0 :     args.calleev().setObject(*rdata.lambda);
        2188               0 :     args.thisv().setUndefined();
        2189                 :
        2190               0 :     Value *sp = args.array();
        2191               0 :     sp[0].setString(matchStr);
        2192               0 :     sp[1].setInt32(fm.match());
        2193               0 :     sp[2].setString(rdata.str);
        2194                 :
        2195               0 :     if (!Invoke(cx, rdata.args))
        2196               0 :         return false;
        2197                 :
        2198               0 :     JSString *repstr = ToString(cx, args.rval());
        2199               0 :     if (!repstr)
        2200               0 :         return false;
        2201                 :
        2202               0 :     JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match());
        2203               0 :     if (!leftSide)
        2204               0 :         return false;
        2205                 :
        2206               0 :     size_t matchLimit = fm.match() + fm.patternLength();
        2207                 :     JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit,
        2208               0 :                                                 rdata.str->length() - matchLimit);
        2209               0 :     if (!rightSide)
        2210               0 :         return false;
        2211                 :
        2212               0 :     RopeBuilder builder(cx);
        2213               0 :     if (!(builder.append(leftSide) &&
        2214               0 :           builder.append(repstr) &&
        2215               0 :           builder.append(rightSide))) {
        2216               0 :         return false;
        2217                 :     }
        2218                 :
        2219               0 :     outerArgs.rval() = StringValue(builder.result());
        2220               0 :     return true;
        2221                 : }
        2222                 :
        2223                 : static const uint32_t ReplaceOptArg = 2;
        2224                 :
        2225                 : JSBool
        2226          544499 : js::str_replace(JSContext *cx, unsigned argc, Value *vp)
        2227                 : {
        2228          544499 :     CallArgs args = CallArgsFromVp(argc, vp);
        2229                 :
        2230         1088998 :     ReplaceData rdata(cx);
        2231          544499 :     rdata.str = ThisToStringForStringProto(cx, args);
        2232          544499 :     if (!rdata.str)
        2233               0 :         return false;
        2234                 :
        2235          544499 :     if (!rdata.g.init(cx, args))
        2236              18 :         return false;
        2237                 :
        2238                 :     /* Extract replacement string/function. */
        2239          544481 :     if (args.length() >= ReplaceOptArg && js_IsCallable(args[1])) {
        2240            2832 :         rdata.lambda = &args[1].toObject();
        2241            2832 :         rdata.elembase = NULL;
        2242            2832 :         rdata.repstr = NULL;
        2243            2832 :         rdata.dollar = rdata.dollarEnd = NULL;
        2244                 :
        2245            2832 :         if (rdata.lambda->isFunction()) {
        2246            2832 :             JSFunction *fun = rdata.lambda->toFunction();
        2247            2832 :             if (fun->isInterpreted()) {
        2248                 :                 /*
        2249                 :                  * Pattern match the script to check if it is is indexing into a
        2250                 :                  * particular object, e.g. 'function(a) { return b[a]; }'.  Avoid
        2251                 :                  * calling the script in such cases, which are used by javascript
        2252                 :                  * packers (particularly the popular Dean Edwards packer) to efficiently
        2253                 :                  * encode large scripts.  We only handle the code patterns generated
        2254                 :                  * by such packers here.
        2255                 :                  */
        2256            2742 :                 JSScript *script = fun->script();
        2257            2742 :                 jsbytecode *pc = script->code;
        2258                 :
        2259            2742 :                 Value table = UndefinedValue();
        2260            2742 :                 if (JSOp(*pc) == JSOP_GETFCSLOT) {
        2261             261 :                     table = fun->getFlatClosureUpvar(GET_UINT16(pc));
        2262             261 :                     pc += JSOP_GETFCSLOT_LENGTH;
        2263                 :                 }
        2264                 :
        2265            3138 :                 if (table.isObject() &&
        2266             198 :                     JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
        2267              99 :                     JSOp(pc[JSOP_GETARG_LENGTH]) == JSOP_GETELEM &&
        2268              99 :                     JSOp(pc[JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH]) == JSOP_RETURN) {
        2269              99 :                     Class *clasp = table.toObject().getClass();
        2270             297 :                     if (clasp->isNative() &&
        2271              99 :                         !clasp->ops.lookupProperty &&
        2272              99 :                         !clasp->ops.getProperty) {
        2273              99 :                         rdata.elembase = &table.toObject();
        2274                 :                     }
        2275                 :                 }
        2276                 :             }
        2277                 :         }
        2278                 :     } else {
        2279          541649 :         rdata.lambda = NULL;
        2280          541649 :         rdata.elembase = NULL;
        2281          541649 :         rdata.repstr = ArgToRootedString(cx, args, 1);
        2282          541649 :         if (!rdata.repstr)
        2283               0 :             return false;
        2284                 :
        2285                 :         /* We're about to store pointers into the middle of our string. */
        2286          541649 :         JSFixedString *fixed = rdata.repstr->ensureFixed(cx);
        2287          541649 :         if (!fixed)
        2288               0 :             return false;
        2289          541649 :         rdata.dollarEnd = fixed->chars() + fixed->length();
        2290          541649 :         rdata.dollar = js_strchr_limit(fixed->chars(), '$', rdata.dollarEnd);
        2291                 :     }
        2292                 :
        2293                 :     /*
        2294                 :      * Unlike its |String.prototype| brethren, |replace| doesn't convert
        2295                 :      * its input to a regular expression. (Even if it contains metachars.)
        2296                 :      *
        2297                 :      * However, if the user invokes our (non-standard) |flags| argument
        2298                 :      * extension then we revert to creating a regular expression. Note that
        2299                 :      * this is observable behavior through the side-effect mutation of the
        2300                 :      * |RegExp| statics.
        2301                 :      */
        2302                 :
        2303          544481 :     const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, ReplaceOptArg, args.length(), false);
        2304          544481 :     if (!fm) {
        2305          510788 :         if (cx->isExceptionPending())  /* oom in RopeMatch in tryFlatMatch */
        2306               0 :             return false;
        2307          510788 :         return str_replace_regexp(cx, args, rdata);
        2308                 :     }
        2309                 :
        2310           33693 :     if (fm->match() < 0) {
        2311           20295 :         args.rval() = StringValue(rdata.str);
        2312           20295 :         return true;
        2313                 :     }
        2314                 :
        2315           13398 :     if (rdata.lambda)
        2316               0 :         return str_replace_flat_lambda(cx, args, rdata, *fm);
        2317                 :
        2318                 :     /*
        2319                 :      * Note: we could optimize the text.length == pattern.length case if we wanted,
        2320                 :      * even in the presence of dollar metachars.
        2321                 :      */
        2322           13398 :     if (rdata.dollar)
        2323              18 :         return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, &args);
        2324                 :
        2325           13380 :     return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, &args);
        2326                 : }
        2327                 :
        2328                 : class SplitMatchResult {
        2329                 :     size_t endIndex_;
        2330                 :     size_t length_;
        2331                 :
        2332                 :   public:
        2333           34592 :     void setFailure() {
        2334                 :         JS_STATIC_ASSERT(SIZE_MAX > JSString::MAX_LENGTH);
        2335           34592 :         endIndex_ = SIZE_MAX;
        2336           34592 :     }
        2337         1067570 :     bool isFailure() const {
        2338         1067570 :         return (endIndex_ == SIZE_MAX);
        2339                 :     }
        2340          344326 :     size_t endIndex() const {
        2341          344326 :         JS_ASSERT(!isFailure());
        2342          344326 :         return endIndex_;
        2343                 :     }
        2344          344326 :     size_t length() const {
        2345          344326 :         JS_ASSERT(!isFailure());
        2346          344326 :         return length_;
        2347                 :     }
        2348          344326 :     void setResult(size_t length, size_t endIndex) {
        2349          344326 :         length_ = length;
        2350          344326 :         endIndex_ = endIndex;
        2351          344326 :     }
        2352                 : };
        2353                 :
        2354                 : template<class Matcher>
        2355                 : static JSObject *
        2356           44357 : SplitHelper(JSContext *cx, JSLinearString *str, uint32_t limit, Matcher splitMatch, TypeObject *type)
        2357                 : {
        2358           44357 :     size_t strLength = str->length();
        2359                 :     SplitMatchResult result;
        2360                 :
        2361                 :     /* Step 11. */
        2362           44357 :     if (strLength == 0) {
        2363            3542 :         if (!splitMatch(cx, str, 0, &result))
        2364               0 :             return NULL;
        2365                 :
        2366                 :         /*
        2367                 :          * NB: Unlike in the non-empty string case, it's perfectly fine
        2368                 :          *     (indeed the spec requires it) if we match at the end of the
        2369                 :          *     string.  Thus these cases should hold:
        2370                 :          *
        2371                 :          *   var a = "".split("");
        2372                 :          *   assertEq(a.length, 0);
        2373                 :          *   var b = "".split(/.?/);
        2374                 :          *   assertEq(b.length, 0);
        2375                 :          */
        2376            3542 :         if (!result.isFailure())
        2377               0 :             return NewDenseEmptyArray(cx);
        2378                 :
        2379            3542 :         Value v = StringValue(str);
        2380            3542 :         return NewDenseCopiedArray(cx, 1, &v);
        2381                 :     }
        2382                 :
        2383                 :     /* Step 12. */
        2384           40815 :     size_t lastEndIndex = 0;
        2385           40815 :     size_t index = 0;
        2386                 :
        2387                 :     /* Step 13. */
        2388           81630 :     AutoValueVector splits(cx);
        2389                 :
        2390          425956 :     while (index < strLength) {
        2391                 :         /* Step 13(a). */
        2392          375376 :         if (!splitMatch(cx, str, index, &result))
        2393               0 :             return NULL;
        2394                 :
        2395                 :         /*
        2396                 :          * Step 13(b).
        2397                 :          *
        2398                 :          * Our match algorithm differs from the spec in that it returns the
        2399                 :          * next index at which a match happens.  If no match happens we're
        2400                 :          * done.
        2401                 :          *
        2402                 :          * But what if the match is at the end of the string (and the string is
        2403                 :          * not empty)?  Per 13(c)(ii) this shouldn't be a match, so we have to
        2404                 :          * specially exclude it.  Thus this case should hold:
        2405                 :          *
        2406                 :          *   var a = "abc".split(/\b/);
        2407                 :          *   assertEq(a.length, 1);
        2408                 :          *   assertEq(a[0], "abc");
        2409                 :          */
        2410          375376 :         if (result.isFailure())
        2411           31050 :             break;
        2412                 :
        2413                 :         /* Step 13(c)(i). */
        2414          344326 :         size_t sepLength = result.length();
        2415          344326 :         size_t endIndex = result.endIndex();
        2416          344326 :         if (sepLength == 0 && endIndex == strLength)
        2417               0 :             break;
        2418                 :
        2419                 :         /* Step 13(c)(ii). */
        2420          344326 :         if (endIndex == lastEndIndex) {
        2421           99298 :             index++;
        2422           99298 :             continue;
        2423                 :         }
        2424                 :
        2425                 :         /* Step 13(c)(iii). */
        2426          245028 :         JS_ASSERT(lastEndIndex < endIndex);
        2427          245028 :         JS_ASSERT(sepLength <= strLength);
        2428          245028 :         JS_ASSERT(lastEndIndex + sepLength <= endIndex);
        2429                 :
        2430                 :         /* Steps 13(c)(iii)(1-3). */
        2431          245028 :         size_t subLength = size_t(endIndex - sepLength - lastEndIndex);
        2432          245028 :         JSString *sub = js_NewDependentString(cx, str, lastEndIndex, subLength);
        2433          245028 :         if (!sub || !splits.append(StringValue(sub)))
        2434               0 :             return NULL;
        2435                 :
        2436                 :         /* Step 13(c)(iii)(4). */
        2437          245028 :         if (splits.length() == limit)
        2438               0 :             return NewDenseCopiedArray(cx, splits.length(), splits.begin());
        2439                 :
        2440                 :         /* Step 13(c)(iii)(5). */
        2441          245028 :         lastEndIndex = endIndex;
        2442                 :
        2443                 :         /* Step 13(c)(iii)(6-7). */
        2444                 :         if (Matcher::returnsCaptures) {
        2445           48648 :             RegExpStatics *res = cx->regExpStatics();
        2446           48648 :             for (size_t i = 0; i < res->parenCount(); i++) {
        2447                 :                 /* Steps 13(c)(iii)(7)(a-c). */
        2448               0 :                 if (res->pairIsPresent(i + 1)) {
        2449                 :                     JSSubString parsub;
        2450               0 :                     res->getParen(i + 1, &parsub);
        2451               0 :                     sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
        2452               0 :                     if (!sub || !splits.append(StringValue(sub)))
        2453               0 :                         return NULL;
        2454                 :                 } else {
        2455                 :                     /* Only string entries have been accounted for so far. */
        2456               0 :                     AddTypeProperty(cx, type, NULL, UndefinedValue());
        2457               0 :                     if (!splits.append(UndefinedValue()))
        2458               0 :                         return NULL;
        2459                 :                 }
        2460                 :
        2461                 :                 /* Step 13(c)(iii)(7)(d). */
        2462               0 :                 if (splits.length() == limit)
        2463               0 :                     return NewDenseCopiedArray(cx, splits.length(), splits.begin());
        2464                 :             }
        2465                 :         }
        2466                 :
        2467                 :         /* Step 13(c)(iii)(8). */
        2468          245028 :         index = lastEndIndex;
        2469                 :     }
        2470                 :
        2471                 :     /* Steps 14-15. */
        2472           40815 :     JSString *sub = js_NewDependentString(cx, str, lastEndIndex, strLength - lastEndIndex);
        2473           40815 :     if (!sub || !splits.append(StringValue(sub)))
        2474               0 :         return NULL;
        2475                 :
        2476                 :     /* Step 16. */
        2477           40815 :     return NewDenseCopiedArray(cx, splits.length(), splits.begin());
        2478                 : }
        2479                 :
        2480                 : /*
        2481                 :  * The SplitMatch operation from ES5 15.5.4.14 is implemented using different
        2482                 :  * paths for regular expression and string separators.
        2483                 :  *
        2484                 :  * The algorithm differs from the spec in that the we return the next index at
        2485                 :  * which a match happens.
        2486                 :  */
        2487                 : class SplitRegExpMatcher
        2488                 : {
        2489                 :     RegExpShared &re;
        2490                 :     RegExpStatics *res;
        2491                 :
        2492                 :   public:
        2493           20359 :     SplitRegExpMatcher(RegExpShared &re, RegExpStatics *res) : re(re), res(res) {}
        2494                 :
        2495                 :     static const bool returnsCaptures = true;
        2496                 :
        2497           68882 :     bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *result)
        2498                 :     {
        2499           68882 :         Value rval = UndefinedValue();
        2500           68882 :         const jschar *chars = str->chars();
        2501           68882 :         size_t length = str->length();
        2502           68882 :         if (!ExecuteRegExp(cx, res, re, str, chars, length, &index, RegExpTest, &rval))
        2503               0 :             return false;
        2504           68882 :         if (!rval.isTrue()) {
        2505           20234 :             result->setFailure();
        2506           20234 :             return true;
        2507                 :         }
        2508                 :         JSSubString sep;
        2509           48648 :         res->getLastMatch(&sep);
        2510                 :
        2511           48648 :         result->setResult(sep.length, index);
        2512           48648 :         return true;
        2513                 :     }
        2514                 : };
        2515                 :
        2516                 : class SplitStringMatcher
        2517                 : {
        2518                 :     const jschar *sepChars;
        2519                 :     size_t sepLength;
        2520                 :
        2521                 :   public:
        2522           23998 :     SplitStringMatcher(JSLinearString *sep) {
        2523           23998 :         sepChars = sep->chars();
        2524           23998 :         sepLength = sep->length();
        2525           23998 :     }
        2526                 :
        2527                 :     static const bool returnsCaptures = false;
        2528                 :
        2529          310036 :     bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *res)
        2530                 :     {
        2531          310036 :         JS_ASSERT(index == 0 || index < str->length());
        2532          310036 :         const jschar *chars = str->chars();
        2533          310036 :         int match = StringMatch(chars + index, str->length() - index, sepChars, sepLength);
        2534          310036 :         if (match == -1)
        2535           14358 :             res->setFailure();
        2536                 :         else
        2537          295678 :             res->setResult(sepLength, index + match + sepLength);
        2538          310036 :         return true;
        2539                 :     }
        2540                 : };
        2541                 :
        2542                 : /* ES5 15.5.4.14 */
        2543                 : JSBool
        2544           44375 : js::str_split(JSContext *cx, unsigned argc, Value *vp)
        2545                 : {
        2546           44375 :     CallArgs args = CallArgsFromVp(argc, vp);
        2547                 :
        2548                 :     /* Steps 1-2. */
        2549           44375 :     JSString *str = ThisToStringForStringProto(cx, args);
        2550           44375 :     if (!str)
        2551               0 :         return false;
        2552                 :
        2553           44375 :     TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array);
        2554           44375 :     if (!type)
        2555               0 :         return false;
        2556           44375 :     AddTypeProperty(cx, type, NULL, Type::StringType());
        2557                 :
        2558                 :     /* Step 5: Use the second argument as the split limit, if given. */
        2559                 :     uint32_t limit;
        2560           44375 :     if (args.hasDefined(1)) {
        2561                 :         double d;
        2562               0 :         if (!ToNumber(cx, args[1], &d))
        2563               0 :             return false;
        2564               0 :         limit = js_DoubleToECMAUint32(d);
        2565                 :     } else {
        2566           44375 :         limit = UINT32_MAX;
        2567                 :     }
        2568                 :
        2569                 :     /* Step 8. */
        2570           88750 :     RegExpGuard re;
        2571           44375 :     JSLinearString *sepstr = NULL;
        2572           44375 :     bool sepDefined = args.hasDefined(0);
        2573           44375 :     if (sepDefined) {
        2574           44375 :         if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
        2575           20359 :             if (!RegExpToShared(cx, args[0].toObject(), &re))
        2576               0 :                 return false;
        2577                 :         } else {
        2578           24016 :             sepstr = ArgToRootedString(cx, args, 0);
        2579           24016 :             if (!sepstr)
        2580              18 :                 return false;
        2581                 :         }
        2582                 :     }
        2583                 :
        2584                 :     /* Step 9. */
        2585           44357 :     if (limit == 0) {
        2586               0 :         JSObject *aobj = NewDenseEmptyArray(cx);
        2587               0 :         if (!aobj)
        2588               0 :             return false;
        2589               0 :         aobj->setType(type);
        2590               0 :         args.rval() = ObjectValue(*aobj);
        2591               0 :         return true;
        2592                 :     }
        2593                 :
        2594                 :     /* Step 10. */
        2595           44357 :     if (!sepDefined) {
        2596               0 :         Value v = StringValue(str);
        2597               0 :         JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
        2598               0 :         if (!aobj)
        2599               0 :             return false;
        2600               0 :         aobj->setType(type);
        2601               0 :         args.rval() = ObjectValue(*aobj);
        2602               0 :         return true;
        2603                 :     }
        2604           44357 :     JSLinearString *strlin = str->ensureLinear(cx);
        2605           44357 :     if (!strlin)
        2606               0 :         return false;
        2607                 :
        2608                 :     /* Steps 11-15. */
        2609                 :     JSObject *aobj;
        2610           44357 :     if (!re.initialized())
        2611           23998 :         aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr), type);
        2612                 :     else
        2613           20359 :         aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(*re, cx->regExpStatics()), type);
        2614           44357 :     if (!aobj)
        2615               0 :         return false;
        2616                 :
        2617                 :     /* Step 16. */
        2618           44357 :     aobj->setType(type);
        2619           44357 :     args.rval() = ObjectValue(*aobj);
        2620           44357 :     return true;
        2621                 : }
        2622                 :
        2623                 : #if JS_HAS_PERL_SUBSTR
        2624                 : static JSBool
        2625           32309 : str_substr(JSContext *cx, unsigned argc, Value *vp)
        2626                 : {
        2627           32309 :     CallArgs args = CallArgsFromVp(argc, vp);
        2628           32309 :     JSString *str = ThisToStringForStringProto(cx, args);
        2629           32309 :     if (!str)
        2630               0 :         return false;
        2631                 :
        2632                 :     int32_t length, len, begin;
        2633           32309 :     if (args.length() > 0) {
        2634           32309 :         length = int32_t(str->length());
        2635           32309 :         if (!ValueToIntegerRange(cx, args[0], &begin))
        2636               0 :             return false;
        2637                 :
        2638           32309 :         if (begin >= length) {
        2639             142 :             str = cx->runtime->emptyString;
        2640             142 :             goto out;
        2641                 :         }
        2642           32167 :         if (begin < 0) {
        2643             332 :             begin += length; /* length + INT_MIN will always be less than 0 */
        2644             332 :             if (begin < 0)
        2645               9 :                 begin = 0;
        2646                 :         }
        2647                 :
        2648           32167 :         if (args.hasDefined(1)) {
        2649           29368 :             if (!ValueToIntegerRange(cx, args[1], &len))
        2650               0 :                 return false;
        2651                 :
        2652           29368 :             if (len <= 0) {
        2653               3 :                 str = cx->runtime->emptyString;
        2654               3 :                 goto out;
        2655                 :             }
        2656                 :
        2657           29365 :             if (uint32_t(length) < uint32_t(begin + len))
        2658            1854 :                 len = length - begin;
        2659                 :         } else {
        2660            2799 :             len = length - begin;
        2661                 :         }
        2662                 :
        2663           32164 :         str = js_NewDependentString(cx, str, size_t(begin), size_t(len));
        2664           32164 :         if (!str)
        2665               0 :             return false;
        2666                 :     }
        2667                 :
        2668                 : out:
        2669           32309 :     args.rval() = StringValue(str);
        2670           32309 :     return true;
        2671                 : }
        2672                 : #endif /* JS_HAS_PERL_SUBSTR */
        2673                 :
        2674                 : /*
        2675                 :  * Python-esque sequence operations.
        2676                 :  */
        2677                 : static JSBool
        2678             353 : str_concat(JSContext *cx, unsigned argc, Value *vp)
        2679                 : {
        2680             353 :     CallArgs args = CallArgsFromVp(argc, vp);
        2681             353 :     JSString *str = ThisToStringForStringProto(cx, args);
        2682             353 :     if (!str)
        2683               0 :         return false;
        2684                 :
        2685             697 :     for (unsigned i = 0; i < args.length(); i++) {
        2686             353 :         JSString *argStr = ToString(cx, args[i]);
        2687             353 :         if (!argStr)
        2688               9 :             return false;
        2689                 :
        2690             344 :         str = js_ConcatStrings(cx, str, argStr);
        2691             344 :         if (!str)
        2692               0 :             return false;
        2693                 :     }
        2694                 :
        2695             344 :     args.rval() = StringValue(str);
        2696             344 :     return true;
        2697                 : }
        2698                 :
        2699                 : static JSBool
        2700           40190 : str_slice(JSContext *cx, unsigned argc, Value *vp)
        2701                 : {
        2702           40190 :     CallArgs args = CallArgsFromVp(argc, vp);
        2703                 :
        2704           40190 :     if (args.length() == 1 && args.thisv().isString() && args[0].isInt32()) {
        2705                 :         size_t begin, end, length;
        2706                 :
        2707           37478 :         JSString *str = args.thisv().toString();
        2708           37478 :         begin = args[0].toInt32();
        2709           37478 :         end = str->length();
        2710           37478 :         if (begin <= end) {
        2711            3748 :             length = end - begin;
        2712            3748 :             if (length == 0) {
        2713             830 :                 str = cx->runtime->emptyString;
        2714                 :             } else {
        2715                 :                 str = (length == 1)
        2716               0 :                       ? cx->runtime->staticStrings.getUnitStringForElement(cx, str, begin)
        2717            2918 :                       : js_NewDependentString(cx, str, begin, length);
        2718            2918 :                 if (!str)
        2719               0 :                     return false;
        2720                 :             }
        2721            3748 :             args.rval() = StringValue(str);
        2722            3748 :             return true;
        2723                 :         }
        2724                 :     }
        2725                 :
        2726           36442 :     JSString *str = ThisToStringForStringProto(cx, args);
        2727           36442 :     if (!str)
        2728               0 :         return false;
        2729                 :
        2730           36442 :     if (args.length() != 0) {
        2731                 :         double begin, end, length;
        2732                 :
        2733           36442 :         if (!ToInteger(cx, args[0], &begin))
        2734               0 :             return false;
        2735           36442 :         length = str->length();
        2736           36442 :         if (begin < 0) {
        2737           33730 :             begin += length;
        2738           33730 :             if (begin < 0)
        2739               0 :                 begin = 0;
        2740            2712 :         } else if (begin > length) {
        2741               0 :             begin = length;
        2742                 :         }
        2743                 :
        2744           36442 :         if (args.hasDefined(1)) {
        2745            2712 :             if (!ToInteger(cx, args[1], &end))
        2746               0 :                 return false;
        2747            2712 :             if (end < 0) {
        2748             690 :                 end += length;
        2749             690 :                 if (end < 0)
        2750               0 :                     end = 0;
        2751            2022 :             } else if (end > length) {
        2752               2 :                 end = length;
        2753                 :             }
        2754            2712 :             if (end < begin)
        2755               0 :                 end = begin;
        2756                 :         } else {
        2757           33730 :             end = length;
        2758                 :         }
        2759                 :
        2760                 :         str = js_NewDependentString(cx, str,
        2761                 :                                     (size_t)begin,
        2762           36442 :                                     (size_t)(end - begin));
        2763           36442 :         if (!str)
        2764               0 :             return false;
        2765                 :     }
        2766           36442 :     args.rval() = StringValue(str);
        2767           36442 :     return true;
        2768                 : }
        2769                 :
        2770                 : #if JS_HAS_STR_HTML_HELPERS
        2771                 : /*
        2772                 :  * HTML composition aids.
        2773                 :  */
        2774                 : static bool
        2775               0 : tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end,
        2776                 :        CallReceiver call)
        2777                 : {
        2778               0 :     JSString *thisstr = ThisToStringForStringProto(cx, call);
        2779               0 :     if (!thisstr)
        2780               0 :         return false;
        2781                 :
        2782               0 :     JSLinearString *str = thisstr->ensureLinear(cx);
        2783               0 :     if (!str)
        2784               0 :         return false;
        2785                 :
        2786               0 :     if (!end)
        2787               0 :         end = begin;
        2788                 :
        2789               0 :     size_t beglen = strlen(begin);
        2790               0 :     size_t taglen = 1 + beglen + 1;                     /* '<begin' + '>' */
        2791               0 :     size_t parlen = 0; /* Avoid warning. */
        2792               0 :     if (param) {
        2793               0 :         parlen = param->length();
        2794               0 :         taglen += 2 + parlen + 1;                       /* '="param"' */
        2795                 :     }
        2796               0 :     size_t endlen = strlen(end);
        2797               0 :     taglen += str->length() + 2 + endlen + 1;           /* 'str</end>' */
        2798                 :
        2799               0 :     if (taglen >= ~(size_t)0 / sizeof(jschar)) {
        2800               0 :         js_ReportAllocationOverflow(cx);
        2801               0 :         return false;
        2802                 :     }
        2803                 :
        2804               0 :     jschar *tagbuf = (jschar *) cx->malloc_((taglen + 1) * sizeof(jschar));
        2805               0 :     if (!tagbuf)
        2806               0 :         return false;
        2807                 :
        2808               0 :     size_t j = 0;
        2809               0 :     tagbuf[j++] = '<';
        2810               0 :     for (size_t i = 0; i < beglen; i++)
        2811               0 :         tagbuf[j++] = (jschar)begin[i];
        2812               0 :     if (param) {
        2813               0 :         tagbuf[j++] = '=';
        2814               0 :         tagbuf[j++] = '"';
        2815               0 :         js_strncpy(&tagbuf[j], param->chars(), parlen);
        2816               0 :         j += parlen;
        2817               0 :         tagbuf[j++] = '"';
        2818                 :     }
        2819               0 :     tagbuf[j++] = '>';
        2820                 :
        2821               0 :     js_strncpy(&tagbuf[j], str->chars(), str->length());
        2822               0 :     j += str->length();
        2823               0 :     tagbuf[j++] = '<';
        2824               0 :     tagbuf[j++] = '/';
        2825               0 :     for (size_t i = 0; i < endlen; i++)
        2826               0 :         tagbuf[j++] = (jschar)end[i];
        2827               0 :     tagbuf[j++] = '>';
        2828               0 :     JS_ASSERT(j == taglen);
        2829               0 :     tagbuf[j] = 0;
        2830                 :
        2831               0 :     JSString *retstr = js_NewString(cx, tagbuf, taglen);
        2832               0 :     if (!retstr) {
        2833               0 :         Foreground::free_((char *)tagbuf);
        2834               0 :         return false;
        2835                 :     }
        2836               0 :     call.rval() = StringValue(retstr);
        2837               0 :     return true;
        2838                 : }
        2839                 :
        2840                 : static JSBool
        2841               0 : tagify_value(JSContext *cx, CallArgs args, const char *begin, const char *end)
        2842                 : {
        2843               0 :     JSLinearString *param = ArgToRootedString(cx, args, 0);
        2844               0 :     if (!param)
        2845               0 :         return false;
        2846                 :
        2847               0 :     return tagify(cx, begin, param, end, args);
        2848                 : }
        2849                 :
        2850                 : static JSBool
        2851               0 : str_bold(JSContext *cx, unsigned argc, Value *vp)
        2852                 : {
        2853               0 :     return tagify(cx, "b", NULL, NULL, CallReceiverFromVp(vp));
        2854                 : }
        2855                 :
        2856                 : static JSBool
        2857               0 : str_italics(JSContext *cx, unsigned argc, Value *vp)
        2858                 : {
        2859               0 :     return tagify(cx, "i", NULL, NULL, CallReceiverFromVp(vp));
        2860                 : }
        2861                 :
        2862                 : static JSBool
        2863               0 : str_fixed(JSContext *cx, unsigned argc, Value *vp)
        2864                 : {
        2865               0 :     return tagify(cx, "tt", NULL, NULL, CallReceiverFromVp(vp));
        2866                 : }
        2867                 :
        2868                 : static JSBool
        2869               0 : str_fontsize(JSContext *cx, unsigned argc, Value *vp)
        2870                 : {
        2871               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "font size", "font");
        2872                 : }
        2873                 :
        2874                 : static JSBool
        2875               0 : str_fontcolor(JSContext *cx, unsigned argc, Value *vp)
        2876                 : {
        2877               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "font color", "font");
        2878                 : }
        2879                 :
        2880                 : static JSBool
        2881               0 : str_link(JSContext *cx, unsigned argc, Value *vp)
        2882                 : {
        2883               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "a href", "a");
        2884                 : }
        2885                 :
        2886                 : static JSBool
        2887               0 : str_anchor(JSContext *cx, unsigned argc, Value *vp)
        2888                 : {
        2889               0 :     return tagify_value(cx, CallArgsFromVp(argc, vp), "a name", "a");
        2890                 : }
        2891                 :
        2892                 : static JSBool
        2893               0 : str_strike(JSContext *cx, unsigned argc, Value *vp)
        2894                 : {
        2895               0 :     return tagify(cx, "strike", NULL, NULL, CallReceiverFromVp(vp));
        2896                 : }
        2897                 :
        2898                 : static JSBool
        2899               0 : str_small(JSContext *cx, unsigned argc, Value *vp)
        2900                 : {
        2901               0 :     return tagify(cx, "small", NULL, NULL, CallReceiverFromVp(vp));
        2902                 : }
        2903                 :
        2904                 : static JSBool
        2905               0 : str_big(JSContext *cx, unsigned argc, Value *vp)
        2906                 : {
        2907               0 :     return tagify(cx, "big", NULL, NULL, CallReceiverFromVp(vp));
        2908                 : }
        2909                 :
        2910                 : static JSBool
        2911               0 : str_blink(JSContext *cx, unsigned argc, Value *vp)
        2912                 : {
        2913               0 :     return tagify(cx, "blink", NULL, NULL, CallReceiverFromVp(vp));
        2914                 : }
        2915                 :
        2916                 : static JSBool
        2917               0 : str_sup(JSContext *cx, unsigned argc, Value *vp)
        2918                 : {
        2919               0 :     return tagify(cx, "sup", NULL, NULL, CallReceiverFromVp(vp));
        2920                 : }
        2921                 :
        2922                 : static JSBool
        2923               0 : str_sub(JSContext *cx, unsigned argc, Value *vp)
        2924                 : {
        2925               0 :     return tagify(cx, "sub", NULL, NULL, CallReceiverFromVp(vp));
        2926                 : }
        2927                 : #endif /* JS_HAS_STR_HTML_HELPERS */
        2928                 :
        2929                 : static JSFunctionSpec string_methods[] = {
        2930                 : #if JS_HAS_TOSOURCE
        2931                 :     JS_FN("quote",             str_quote,             0,JSFUN_GENERIC_NATIVE),
        2932                 :     JS_FN(js_toSource_str,     str_toSource,          0,0),
        2933                 : #endif
        2934                 :
        2935                 :     /* Java-like methods. */
        2936                 :     JS_FN(js_toString_str,     js_str_toString,       0,0),
        2937                 :     JS_FN(js_valueOf_str,      js_str_toString,       0,0),
        2938                 :     JS_FN("substring",         str_substring,         2,JSFUN_GENERIC_NATIVE),
        2939                 :     JS_FN("toLowerCase",       str_toLowerCase,       0,JSFUN_GENERIC_NATIVE),
        2940                 :     JS_FN("toUpperCase",       str_toUpperCase,       0,JSFUN_GENERIC_NATIVE),
        2941                 :     JS_FN("charAt",            js_str_charAt,         1,JSFUN_GENERIC_NATIVE),
        2942                 :     JS_FN("charCodeAt",        js_str_charCodeAt,     1,JSFUN_GENERIC_NATIVE),
        2943                 :     JS_FN("indexOf",           str_indexOf,           1,JSFUN_GENERIC_NATIVE),
        2944                 :     JS_FN("lastIndexOf",       str_lastIndexOf,       1,JSFUN_GENERIC_NATIVE),
        2945                 :     JS_FN("trim",              str_trim,              0,JSFUN_GENERIC_NATIVE),
        2946                 :     JS_FN("trimLeft",          str_trimLeft,          0,JSFUN_GENERIC_NATIVE),
        2947                 :     JS_FN("trimRight",         str_trimRight,         0,JSFUN_GENERIC_NATIVE),
        2948                 :     JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE),
        2949                 :     JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE),
        2950                 :     JS_FN("localeCompare",     str_localeCompare,     1,JSFUN_GENERIC_NATIVE),
        2951                 :
        2952                 :     /* Perl-ish methods (search is actually Python-esque). */
        2953                 :     JS_FN("match",             str_match,             1,JSFUN_GENERIC_NATIVE),
        2954                 :     JS_FN("search",            str_search,            1,JSFUN_GENERIC_NATIVE),
        2955                 :     JS_FN("replace",           str_replace,           2,JSFUN_GENERIC_NATIVE),
        2956                 :     JS_FN("split",             str_split,             2,JSFUN_GENERIC_NATIVE),
        2957                 : #if JS_HAS_PERL_SUBSTR
        2958                 :     JS_FN("substr",            str_substr,            2,JSFUN_GENERIC_NATIVE),
        2959                 : #endif
        2960                 :
        2961                 :     /* Python-esque sequence methods. */
        2962                 :     JS_FN("concat",            str_concat,            1,JSFUN_GENERIC_NATIVE),
        2963                 :     JS_FN("slice",             str_slice,             2,JSFUN_GENERIC_NATIVE),
        2964                 :
        2965                 :     /* HTML string methods. */
        2966                 : #if JS_HAS_STR_HTML_HELPERS
        2967                 :     JS_FN("bold",              str_bold,              0,0),
        2968                 :     JS_FN("italics",           str_italics,           0,0),
        2969                 :     JS_FN("fixed",             str_fixed,             0,0),
        2970                 :     JS_FN("fontsize",          str_fontsize,          1,0),
        2971                 :     JS_FN("fontcolor",         str_fontcolor,         1,0),
        2972                 :     JS_FN("link",              str_link,              1,0),
        2973                 :     JS_FN("anchor",            str_anchor,            1,0),
        2974                 :     JS_FN("strike",            str_strike,            0,0),
        2975                 :     JS_FN("small",             str_small,             0,0),
        2976                 :     JS_FN("big",               str_big,               0,0),
        2977                 :     JS_FN("blink",             str_blink,             0,0),
        2978                 :     JS_FN("sup",               str_sup,               0,0),
        2979                 :     JS_FN("sub",               str_sub,               0,0),
        2980                 : #endif
        2981                 :
        2982                 :     JS_FS_END
        2983                 : };
        2984                 :
        2985                 : JSBool
        2986          350746 : js_String(JSContext *cx, unsigned argc, Value *vp)
        2987                 : {
        2988          350746 :     CallArgs args = CallArgsFromVp(argc, vp);
        2989                 :
        2990                 :     JSString *str;
        2991          350746 :     if (args.length() > 0) {
        2992          350304 :         str = ToString(cx, args[0]);
        2993          350304 :         if (!str)
        2994               0 :             return false;
        2995                 :     } else {
        2996             442 :         str = cx->runtime->emptyString;
        2997                 :     }
        2998                 :
        2999          350746 :     if (IsConstructing(args)) {
        3000          200551 :         StringObject *strobj = StringObject::create(cx, str);
        3001          200551 :         if (!strobj)
        3002               0 :             return false;
        3003          200551 :         args.rval() = ObjectValue(*strobj);
        3004          200551 :         return true;
        3005                 :     }
        3006                 :
        3007          150195 :     args.rval() = StringValue(str);
        3008          150195 :     return true;
        3009                 : }
        3010                 :
        3011                 : JSBool
        3012         2759798 : js::str_fromCharCode(JSContext *cx, unsigned argc, Value *vp)
        3013                 : {
        3014         2759798 :     CallArgs args = CallArgsFromVp(argc, vp);
        3015                 :
        3016         2759798 :     JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
        3017         2759798 :     if (args.length() == 1) {
        3018                 :         uint16_t code;
        3019         1850378 :         if (!ValueToUint16(cx, args[0], &code))
        3020               9 :             return JS_FALSE;
        3021         1850369 :         if (StaticStrings::hasUnit(code)) {
        3022          673676 :             args.rval() = StringValue(cx->runtime->staticStrings.getUnit(code));
        3023          673676 :             return JS_TRUE;
        3024                 :         }
        3025         1176693 :         args[0].setInt32(code);
        3026                 :     }
        3027         2086113 :     jschar *chars = (jschar *) cx->malloc_((args.length() + 1) * sizeof(jschar));
        3028         2086113 :     if (!chars)
        3029               0 :         return JS_FALSE;
        3030        16256268 :     for (unsigned i = 0; i < args.length(); i++) {
        3031                 :         uint16_t code;
        3032        14170155 :         if (!ValueToUint16(cx, args[i], &code)) {
        3033               0 :             cx->free_(chars);
        3034               0 :             return JS_FALSE;
        3035                 :         }
        3036        14170155 :         chars[i] = (jschar)code;
        3037                 :     }
        3038         2086113 :     chars[args.length()] = 0;
        3039         2086113 :     JSString *str = js_NewString(cx, chars, args.length());
        3040         2086113 :     if (!str) {
        3041               0 :         cx->free_(chars);
        3042               0 :         return JS_FALSE;
        3043                 :     }
        3044                 :
        3045         2086113 :     args.rval() = StringValue(str);
        3046         2086113 :     return JS_TRUE;
        3047                 : }
        3048                 :
        3049                 : static JSFunctionSpec string_static_methods[] = {
        3050                 :     JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
        3051                 :     JS_FS_END
        3052                 : };
        3053                 :
        3054                 : Shape *
        3055           19784 : StringObject::assignInitialShape(JSContext *cx)
        3056                 : {
        3057           19784 :     JS_ASSERT(nativeEmpty());
        3058                 :
        3059           19784 :     return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
        3060           19784 :                            LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
        3061                 : }
        3062                 :
        3063                 : JSObject *
        3064            7022 : js_InitStringClass(JSContext *cx, JSObject *obj)
        3065                 : {
        3066            7022 :     JS_ASSERT(obj->isNative());
        3067                 :
        3068            7022 :     GlobalObject *global = &obj->asGlobal();
        3069                 :
        3070            7022 :     JSObject *proto = global->createBlankPrototype(cx, &StringClass);
        3071            7022 :     if (!proto || !proto->asString().init(cx, cx->runtime->emptyString))
        3072               0 :         return NULL;
        3073                 :
        3074                 :     /* Now create the String function. */
        3075                 :     JSFunction *ctor = global->createConstructor(cx, js_String, &StringClass,
        3076            7022 :                                                  CLASS_ATOM(cx, String), 1);
        3077            7022 :     if (!ctor)
        3078               0 :         return NULL;
        3079                 :
        3080            7022 :     if (!LinkConstructorAndPrototype(cx, ctor, proto))
        3081               0 :         return NULL;
        3082                 :
        3083           14044 :     if (!DefinePropertiesAndBrand(cx, proto, NULL, string_methods) ||
        3084            7022 :         !DefinePropertiesAndBrand(cx, ctor, NULL, string_static_methods))
        3085                 :     {
        3086               0 :         return NULL;
        3087                 :     }
        3088                 :
        3089                 :     /* Capture normal data properties pregenerated for String objects. */
        3090            7022 :     TypeObject *type = proto->getNewType(cx);
        3091            7022 :     if (!type)
        3092               0 :         return NULL;
        3093            7022 :     AddTypeProperty(cx, type, "length", Type::Int32Type());
        3094                 :
        3095            7022 :     if (!DefineConstructorAndPrototype(cx, global, JSProto_String, ctor, proto))
        3096               0 :         return NULL;
        3097                 :
        3098                 :     /*
        3099                 :      * Define escape/unescape, the URI encode/decode functions, and maybe
        3100                 :      * uneval on the global object.
        3101                 :      */
        3102            7022 :     if (!JS_DefineFunctions(cx, global, string_functions))
        3103               0 :         return NULL;
        3104                 :
        3105            7022 :     return proto;
        3106                 : }
        3107                 :
        3108                 : JSFixedString *
        3109        14485956 : js_NewString(JSContext *cx, jschar *chars, size_t length)
        3110                 : {
        3111        14485956 :     JSFixedString *s = JSFixedString::new_(cx, chars, length);
        3112        14485956 :     if (s)
        3113        14485956 :         Probes::createString(cx, s, length);
        3114        14485956 :     return s;
        3115                 : }
        3116                 :
        3117                 : static JSInlineString *
        3118          146429 : NewShortString(JSContext *cx, const char *chars, size_t length)
        3119                 : {
        3120          146429 :     JS_ASSERT(JSShortString::lengthFits(length));
        3121          146429 :     JSInlineString *str = JSInlineString::lengthFits(length)
        3122                 :                           ? JSInlineString::new_(cx)
        3123          146429 :                           : JSShortString::new_(cx);
        3124          146429 :     if (!str)
        3125               0 :         return NULL;
        3126                 :
        3127          146429 :     jschar *storage = str->init(length);
        3128          146429 :     if (js_CStringsAreUTF8) {
        3129                 : #ifdef DEBUG
        3130               0 :         size_t oldLength = length;
        3131                 : #endif
        3132               0 :         if (!InflateUTF8StringToBuffer(cx, chars, length, storage, &length))
        3133               0 :             return NULL;
        3134               0 :         JS_ASSERT(length <= oldLength);
        3135               0 :         storage[length] = 0;
        3136               0 :         str->resetLength(length);
        3137                 :     } else {
        3138          146429 :         size_t n = length;
        3139          146429 :         jschar *p = storage;
        3140         1115211 :         while (n--)
        3141          822353 :             *p++ = (unsigned char)*chars++;
        3142          146429 :         *p = 0;
        3143                 :     }
        3144          146429 :     Probes::createString(cx, str, length);
        3145          146429 :     return str;
        3146                 : }
        3147                 :
        3148                 : JSLinearString *
        3149         2650466 : js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t length)
        3150                 : {
        3151         2650466 :     if (length == 0)
        3152           48157 :         return cx->runtime->emptyString;
        3153                 :
        3154         2602309 :     JSLinearString *base = baseArg->ensureLinear(cx);
        3155         2602309 :     if (!base)
        3156               0 :         return NULL;
        3157                 :
        3158         2602309 :     if (start == 0 && length == base->length())
        3159          130245 :         return base;
        3160                 :
        3161         2472064 :     const jschar *chars = base->chars() + start;
        3162                 :
        3163         2472064 :     if (JSLinearString *staticStr = cx->runtime->staticStrings.lookup(chars, length))
        3164         1471014 :         return staticStr;
        3165                 :
        3166         1001050 :     JSLinearString *s = JSDependentString::new_(cx, base, chars, length);
        3167         1001050 :     Probes::createString(cx, s, length);
        3168         1001050 :     return s;
        3169                 : }
        3170                 :
        3171                 : JSFixedString *
        3172       107692879 : js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
        3173                 : {
        3174       107692879 :     if (JSShortString::lengthFits(n))
        3175        99631798 :         return NewShortString(cx, s, n);
        3176                 :
        3177         8061081 :     jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
        3178         8061081 :     if (!news)
        3179               0 :         return NULL;
        3180         8061081 :     js_strncpy(news, s, n);
        3181         8061081 :     news[n] = 0;
        3182         8061081 :     JSFixedString *str = js_NewString(cx, news, n);
        3183         8061081 :     if (!str)
        3184               0 :         cx->free_(news);
        3185         8061081 :     return str;
        3186                 : }
        3187                 :
        3188                 : JSFixedString *
        3189          318968 : js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
        3190                 : {
        3191          318968 :     if (JSShortString::lengthFits(n))
        3192          146429 :         return NewShortString(cx, s, n);
        3193                 :
        3194          172539 :     jschar *chars = InflateString(cx, s, &n);
        3195          172539 :     if (!chars)
        3196               0 :         return NULL;
        3197          172539 :     JSFixedString *str = js_NewString(cx, chars, n);
        3198          172539 :     if (!str)
        3199               0 :         cx->free_(chars);
        3200          172539 :     return str;
        3201                 : }
        3202                 :
        3203                 : JSFixedString *
        3204           31163 : js_NewStringCopyZ(JSContext *cx, const jschar *s)
        3205                 : {
        3206           31163 :     size_t n = js_strlen(s);
        3207           31163 :     if (JSShortString::lengthFits(n))
        3208           18573 :         return NewShortString(cx, s, n);
        3209                 :
        3210           12590 :     size_t m = (n + 1) * sizeof(jschar);
        3211           12590 :     jschar *news = (jschar *) cx->malloc_(m);
        3212           12590 :     if (!news)
        3213               0 :         return NULL;
        3214           12590 :     js_memcpy(news, s, m);
        3215           12590 :     JSFixedString *str = js_NewString(cx, news, n);
        3216           12590 :     if (!str)
        3217               0 :         cx->free_(news);
        3218           12590 :     return str;
        3219                 : }
        3220                 :
        3221                 : JSFixedString *
        3222          298606 : js_NewStringCopyZ(JSContext *cx, const char *s)
        3223                 : {
        3224          298606 :     return js_NewStringCopyN(cx, s, strlen(s));
        3225                 : }
        3226                 :
        3227                 : const char *
        3228           22021 : js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool asSource)
        3229                 : {
        3230                 :     JSString *str;
        3231                 :
        3232           22021 :     str = (asSource ? js_ValueToSource : ToString)(cx, v);
        3233           22021 :     if (!str)
        3234               0 :         return NULL;
        3235           22021 :     str = js_QuoteString(cx, str, 0);
        3236           22021 :     if (!str)
        3237               0 :         return NULL;
        3238           22021 :     return bytes->encode(cx, str);
        3239                 : }
        3240                 :
        3241                 : JSString *
        3242         7796098 : js::ToStringSlow(JSContext *cx, const Value &arg)
        3243                 : {
        3244                 :     /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
        3245         7796098 :     JS_ASSERT(!arg.isString());
        3246                 :
        3247         7796098 :     Value v = arg;
        3248         7796098 :     if (!ToPrimitive(cx, JSTYPE_STRING, &v))
        3249            5430 :         return NULL;
        3250                 :
        3251                 :     JSString *str;
        3252         7790668 :     if (v.isString()) {
        3253           74986 :         str = v.toString();
        3254         7715682 :     } else if (v.isInt32()) {
        3255         4591134 :         str = js_IntToString(cx, v.toInt32());
        3256         3124548 :     } else if (v.isDouble()) {
        3257         2887118 :         str = js_NumberToString(cx, v.toDouble());
        3258          237430 :     } else if (v.isBoolean()) {
        3259          121861 :         str = js_BooleanToString(cx, v.toBoolean());
        3260          115569 :     } else if (v.isNull()) {
        3261           38205 :         str = cx->runtime->atomState.nullAtom;
        3262                 :     } else {
        3263           77364 :         str = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
        3264                 :     }
        3265         7790668 :     return str;
        3266                 : }
        3267                 :
        3268                 : JS_FRIEND_API(JSString *)
        3269           10881 : js_ValueToSource(JSContext *cx, const Value &v)
        3270                 : {
        3271           10881 :     JS_CHECK_RECURSION(cx, return NULL);
        3272                 :
        3273           10881 :     if (v.isUndefined())
        3274             386 :         return cx->runtime->atomState.void0Atom;
        3275           10495 :     if (v.isString())
        3276            2260 :         return js_QuoteString(cx, v.toString(), '"');
        3277            8235 :     if (v.isPrimitive()) {
        3278                 :         /* Special case to preserve negative zero, _contra_ toString. */
        3279            3752 :         if (v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble())) {
        3280                 :             /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
        3281                 :             static const jschar js_negzero_ucNstr[] = {'-', '0'};
        3282                 :
        3283               9 :             return js_NewStringCopyN(cx, js_negzero_ucNstr, 2);
        3284                 :         }
        3285            3743 :         return ToString(cx, v);
        3286                 :     }
        3287                 :
        3288            4483 :     Value rval = NullValue();
        3289                 :     Value fval;
        3290            4483 :     jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
        3291            4483 :     if (!js_GetMethod(cx, &v.toObject(), id, JSGET_NO_METHOD_BARRIER, &fval))
        3292               0 :         return NULL;
        3293            4483 :     if (js_IsCallable(fval)) {
        3294            4483 :         if (!Invoke(cx, v, fval, 0, NULL, &rval))
        3295              18 :             return NULL;
        3296                 :     }
        3297                 :
        3298            4465 :     return ToString(cx, rval);
        3299                 : }
        3300                 :
        3301                 : namespace js {
        3302                 :
        3303                 : bool
        3304         4124011 : EqualStrings(JSContext *cx, JSString *str1, JSString *str2, bool *result)
        3305                 : {
        3306         4124011 :     if (str1 == str2) {
        3307          266439 :         *result = true;
        3308          266439 :         return true;
        3309                 :     }
        3310                 :
        3311         3857572 :     size_t length1 = str1->length();
        3312         3857572 :     if (length1 != str2->length()) {
        3313         1348800 :         *result = false;
        3314         1348800 :         return true;
        3315                 :     }
        3316                 :
        3317         2508772 :     JSLinearString *linear1 = str1->ensureLinear(cx);
        3318         2508772 :     if (!linear1)
        3319               0 :         return false;
        3320         2508772 :     JSLinearString *linear2 = str2->ensureLinear(cx);
        3321         2508772 :     if (!linear2)
        3322               0 :         return false;
        3323                 :
        3324         2508772 :     *result = PodEqual(linear1->chars(), linear2->chars(), length1);
        3325         2508772 :     return true;
        3326                 : }
        3327                 :
        3328                 : bool
        3329          239094 : EqualStrings(JSLinearString *str1, JSLinearString *str2)
        3330                 : {
        3331          239094 :     if (str1 == str2)
        3332             433 :         return true;
        3333                 :
        3334          238661 :     size_t length1 = str1->length();
        3335          238661 :     if (length1 != str2->length())
        3336          133380 :         return false;
        3337                 :
        3338          105281 :     return PodEqual(str1->chars(), str2->chars(), length1);
        3339                 : }
        3340                 :
        3341                 : }  /* namespace js */
        3342                 :
        3343                 : namespace js {
        3344                 :
        3345                 : static bool
        3346          334574 : CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
        3347                 : {
        3348          334574 :     JS_ASSERT(str1);
        3349          334574 :     JS_ASSERT(str2);
        3350                 :
        3351          334574 :     if (str1 == str2) {
        3352            2557 :         *result = 0;
        3353            2557 :         return true;
        3354                 :     }
        3355                 :
        3356          332017 :     const jschar *s1 = str1->getChars(cx);
        3357          332017 :     if (!s1)
        3358               0 :         return false;
        3359                 :
        3360          332017 :     const jschar *s2 = str2->getChars(cx);
        3361          332017 :     if (!s2)
        3362               0 :         return false;
        3363                 :
        3364          332017 :     return CompareChars(s1, str1->length(), s2, str2->length(), result);
        3365                 : }
        3366                 :
        3367                 : bool
        3368          334574 : CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
        3369                 : {
        3370          334574 :     return CompareStringsImpl(cx, str1, str2, result);
        3371                 : }
        3372                 :
        3373                 : }  /* namespace js */
        3374                 :
        3375                 : namespace js {
        3376                 :
        3377                 : bool
        3378          295540 : StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
        3379                 : {
        3380          295540 :     size_t length = strlen(asciiBytes);
        3381                 : #ifdef DEBUG
        3382         1646953 :     for (size_t i = 0; i != length; ++i)
        3383         1351413 :         JS_ASSERT(unsigned(asciiBytes[i]) <= 127);
        3384                 : #endif
        3385          295540 :     if (length != str->length())
        3386          188596 :         return false;
        3387          106944 :     const jschar *chars = str->chars();
        3388          724974 :     for (size_t i = 0; i != length; ++i) {
        3389          618041 :         if (unsigned(asciiBytes[i]) != unsigned(chars[i]))
        3390              11 :             return false;
        3391                 :     }
        3392          106933 :     return true;
        3393                 : }
        3394                 :
        3395                 : } /* namespacejs */
        3396                 :
        3397                 : size_t
        3398           96760 : js_strlen(const jschar *s)
        3399                 : {
        3400                 :     const jschar *t;
        3401                 :
        3402         1860563 :     for (t = s; *t != 0; t++)
        3403         1763803 :         continue;
        3404           96760 :     return (size_t)(t - s);
        3405                 : }
        3406                 :
        3407                 : jschar *
        3408           27130 : js_strchr(const jschar *s, jschar c)
        3409                 : {
        3410         1091641 :     while (*s != 0) {
        3411         1062292 :         if (*s == c)
        3412           24911 :             return (jschar *)s;
        3413         1037381 :         s++;
        3414                 :     }
        3415            2219 :     return NULL;
        3416                 : }
        3417                 :
        3418                 : jschar *
        3419          551916 : js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
        3420                 : {
        3421         4417333 :     while (s < limit) {
        3422         3317988 :         if (*s == c)
        3423            4487 :             return (jschar *)s;
        3424         3313501 :         s++;
        3425                 :     }
        3426          547429 :     return NULL;
        3427                 : }
        3428                 :
        3429                 : namespace js {
        3430                 :
        3431                 : jschar *
        3432         3022415 : InflateString(JSContext *cx, const char *bytes, size_t *lengthp, FlationCoding fc)
        3433                 : {
        3434                 :     size_t nchars;
        3435                 :     jschar *chars;
        3436         3022415 :     size_t nbytes = *lengthp;
        3437                 :
        3438         3022415 :     if (js_CStringsAreUTF8 || fc == CESU8Encoding) {
        3439           13184 :         if (!InflateUTF8StringToBuffer(cx, bytes, nbytes, NULL, &nchars, fc))
        3440               0 :             goto bad;
        3441           13184 :         chars = (jschar *) cx->malloc_((nchars + 1) * sizeof (jschar));
        3442           13184 :         if (!chars)
        3443               0 :             goto bad;
        3444           13184 :         JS_ALWAYS_TRUE(InflateUTF8StringToBuffer(cx, bytes, nbytes, chars, &nchars, fc));
        3445                 :     } else {
        3446         3009231 :         nchars = nbytes;
        3447         3009231 :         chars = (jschar *) cx->malloc_((nchars + 1) * sizeof(jschar));
        3448         3009231 :         if (!chars)
        3449               0 :             goto bad;
        3450      1038547974 :         for (size_t i = 0; i < nchars; i++)
        3451      1035538743 :             chars[i] = (unsigned char) bytes[i];
        3452                 :     }
        3453         3022415 :     *lengthp = nchars;
        3454         3022415 :     chars[nchars] = 0;
        3455         3022415 :     return chars;
        3456                 :
        3457                 :   bad:
        3458                 :     /*
        3459                 :      * For compatibility with callers of JS_DecodeBytes we must zero lengthp
        3460                 :      * on errors.
        3461                 :      */
        3462               0 :     *lengthp = 0;
        3463               0 :     return NULL;
        3464                 : }
        3465                 :
        3466                 : /*
        3467                 :  * May be called with null cx.
        3468                 :  */
        3469                 : char *
        3470          837044 : DeflateString(JSContext *cx, const jschar *chars, size_t nchars)
        3471                 : {
        3472                 :     size_t nbytes, i;
        3473                 :     char *bytes;
        3474                 :
        3475          837044 :     if (js_CStringsAreUTF8) {
        3476              20 :         nbytes = GetDeflatedStringLength(cx, chars, nchars);
        3477              20 :         if (nbytes == (size_t) -1)
        3478               0 :             return NULL;
        3479              20 :         bytes = (char *) (cx ? cx->malloc_(nbytes + 1) : OffTheBooks::malloc_(nbytes + 1));
        3480              20 :         if (!bytes)
        3481               0 :             return NULL;
        3482              20 :         JS_ALWAYS_TRUE(DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes));
        3483                 :     } else {
        3484          837024 :         nbytes = nchars;
        3485          837024 :         bytes = (char *) (cx ? cx->malloc_(nbytes + 1) : OffTheBooks::malloc_(nbytes + 1));
        3486          837024 :         if (!bytes)
        3487               0 :             return NULL;
        3488        46627627 :         for (i = 0; i < nbytes; i++)
        3489        45790603 :             bytes[i] = (char) chars[i];
        3490                 :     }
        3491          837044 :     bytes[nbytes] = 0;
        3492          837044 :     return bytes;
        3493                 : }
        3494                 :
        3495                 : size_t
        3496         2434611 : GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars)
        3497                 : {
        3498         2434611 :     if (!js_CStringsAreUTF8)
        3499         2434590 :         return nchars;
        3500                 :
        3501              21 :     return GetDeflatedUTF8StringLength(cx, chars, nchars);
        3502                 : }
        3503                 :
        3504                 : /*
        3505                 :  * May be called with null cx through public API, see below.
        3506                 :  */
        3507                 : size_t
        3508             540 : GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars,
        3509                 :                                 size_t nchars, FlationCoding fc)
        3510                 : {
        3511                 :     size_t nbytes;
        3512                 :     const jschar *end;
        3513                 :     unsigned c, c2;
        3514                 :     char buffer[10];
        3515             540 :     bool useCESU8 = fc == CESU8Encoding;
        3516                 :
        3517             540 :     nbytes = nchars;
        3518          217483 :     for (end = chars + nchars; chars != end; chars++) {
        3519          216943 :         c = *chars;
        3520          216943 :         if (c < 0x80)
        3521          216920 :             continue;
        3522              23 :         if (0xD800 <= c && c <= 0xDFFF && !useCESU8) {
        3523                 :             /* Surrogate pair. */
        3524               1 :             chars++;
        3525                 :
        3526                 :             /* nbytes sets 1 length since this is surrogate pair. */
        3527               1 :             nbytes--;
        3528               1 :             if (c >= 0xDC00 || chars == end)
        3529                 :                 goto bad_surrogate;
        3530               1 :             c2 = *chars;
        3531               1 :             if (c2 < 0xDC00 || c2 > 0xDFFF)
        3532                 :                 goto bad_surrogate;
        3533               1 :             c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
        3534                 :         }
        3535              23 :         c >>= 11;
        3536              23 :         nbytes++;
        3537              48 :         while (c) {
        3538               2 :             c >>= 5;
        3539               2 :             nbytes++;
        3540                 :         }
        3541                 :     }
        3542             540 :     return nbytes;
        3543                 :
        3544                 :   bad_surrogate:
        3545               0 :     if (cx) {
        3546               0 :         JS_snprintf(buffer, 10, "0x%x", c);
        3547                 :         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
        3548               0 :                                      NULL, JSMSG_BAD_SURROGATE_CHAR, buffer);
        3549                 :     }
        3550               0 :     return (size_t) -1;
        3551                 : }
        3552                 :
        3553                 : bool
        3554         2426778 : DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen,
        3555                 :                           char *dst, size_t *dstlenp)
        3556                 : {
        3557                 :     size_t dstlen, i;
        3558                 :
        3559         2426778 :     dstlen = *dstlenp;
        3560         2426778 :     if (!js_CStringsAreUTF8) {
        3561         2426757 :         if (srclen > dstlen) {
        3562               0 :             for (i = 0; i < dstlen; i++)
        3563               0 :                 dst[i] = (char) src[i];
        3564               0 :             if (cx) {
        3565                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3566               0 :                                      JSMSG_BUFFER_TOO_SMALL);
        3567                 :             }
        3568               0 :             return JS_FALSE;
        3569                 :         }
        3570       247952340 :         for (i = 0; i < srclen; i++)
        3571       245525583 :             dst[i] = (char) src[i];
        3572         2426757 :         *dstlenp = srclen;
        3573         2426757 :         return JS_TRUE;
        3574                 :     }
        3575                 :
        3576              21 :     return DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp);
        3577                 : }
        3578                 :
        3579                 : bool
        3580             282 : DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen,
        3581                 :                               char *dst, size_t *dstlenp, FlationCoding fc)
        3582                 : {
        3583                 :     size_t i, utf8Len;
        3584                 :     jschar c, c2;
        3585                 :     uint32_t v;
        3586                 :     uint8_t utf8buf[6];
        3587                 :
        3588             282 :     bool useCESU8 = fc == CESU8Encoding;
        3589             282 :     size_t dstlen = *dstlenp;
        3590             282 :     size_t origDstlen = dstlen;
        3591                 :
        3592          109304 :     while (srclen) {
        3593          108740 :         c = *src++;
        3594          108740 :         srclen--;
        3595          108740 :         if ((c >= 0xDC00) && (c <= 0xDFFF) && !useCESU8)
        3596               0 :             goto badSurrogate;
        3597          108740 :         if (c < 0xD800 || c > 0xDBFF || useCESU8) {
        3598          108739 :             v = c;
        3599                 :         } else {
        3600               1 :             if (srclen < 1)
        3601               0 :                 goto badSurrogate;
        3602               1 :             c2 = *src;
        3603               1 :             if ((c2 < 0xDC00) || (c2 > 0xDFFF))
        3604                 :                 goto badSurrogate;
        3605               1 :             src++;
        3606               1 :             srclen--;
        3607               1 :             v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
        3608                 :         }
        3609          108740 :         if (v < 0x0080) {
        3610                 :             /* no encoding necessary - performance hack */
        3611          108728 :             if (dstlen == 0)
        3612               0 :                 goto bufferTooSmall;
        3613          108728 :             *dst++ = (char) v;
        3614          108728 :             utf8Len = 1;
        3615                 :         } else {
        3616              12 :             utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
        3617              12 :             if (utf8Len > dstlen)
        3618               0 :                 goto bufferTooSmall;
        3619              38 :             for (i = 0; i < utf8Len; i++)
        3620              26 :                 *dst++ = (char) utf8buf[i];
        3621                 :         }
        3622          108740 :         dstlen -= utf8Len;
        3623                 :     }
        3624             282 :     *dstlenp = (origDstlen - dstlen);
        3625             282 :     return JS_TRUE;
        3626                 :
        3627                 : badSurrogate:
        3628               0 :     *dstlenp = (origDstlen - dstlen);
        3629                 :     /* Delegate error reporting to the measurement function. */
        3630               0 :     if (cx)
        3631               0 :         GetDeflatedStringLength(cx, src - 1, srclen + 1);
        3632               0 :     return JS_FALSE;
        3633                 :
        3634                 : bufferTooSmall:
        3635               0 :     *dstlenp = (origDstlen - dstlen);
        3636               0 :     if (cx) {
        3637                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3638               0 :                              JSMSG_BUFFER_TOO_SMALL);
        3639                 :     }
        3640               0 :     return JS_FALSE;
        3641                 : }
        3642                 :
        3643                 : bool
        3644        33811732 : InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen,
        3645                 :                           jschar *dst, size_t *dstlenp)
        3646                 : {
        3647                 :     size_t dstlen, i;
        3648                 :
        3649        33811732 :     if (js_CStringsAreUTF8)
        3650           53202 :         return InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp);
        3651                 :
        3652        33758530 :     if (dst) {
        3653        33758530 :         dstlen = *dstlenp;
        3654        33758530 :         if (srclen > dstlen) {
        3655               0 :             for (i = 0; i < dstlen; i++)
        3656               0 :                 dst[i] = (unsigned char) src[i];
        3657               0 :             if (cx) {
        3658                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3659               0 :                                      JSMSG_BUFFER_TOO_SMALL);
        3660                 :             }
        3661               0 :             return JS_FALSE;
        3662                 :         }
        3663       298514436 :         for (i = 0; i < srclen; i++)
        3664       264755906 :             dst[i] = (unsigned char) src[i];
        3665                 :     }
        3666        33758530 :     *dstlenp = srclen;
        3667        33758530 :     return JS_TRUE;
        3668                 : }
        3669                 :
        3670                 : bool
        3671          122322 : InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen,
        3672                 :                               jschar *dst, size_t *dstlenp, FlationCoding fc)
        3673                 : {
        3674                 :     size_t dstlen, origDstlen, offset, j, n;
        3675                 :     uint32_t v;
        3676                 :
        3677          122322 :     dstlen = dst ? *dstlenp : (size_t) -1;
        3678          122322 :     origDstlen = dstlen;
        3679          122322 :     offset = 0;
        3680          122322 :     bool useCESU8 = fc == CESU8Encoding;
        3681                 :
        3682       341397162 :     while (srclen) {
        3683       341152519 :         v = (uint8_t) *src;
        3684       341152519 :         n = 1;
        3685       341152519 :         if (v & 0x80) {
        3686            3878 :             while (v & (0x80 >> n))
        3687            1812 :                 n++;
        3688            1033 :             if (n > srclen)
        3689               0 :                 goto bufferTooSmall;
        3690            1033 :             if (n == 1 || n > 4)
        3691                 :                 goto badCharacter;
        3692            2844 :             for (j = 1; j < n; j++) {
        3693            1812 :                 if ((src[j] & 0xC0) != 0x80)
        3694               0 :                     goto badCharacter;
        3695                 :             }
        3696            1032 :             v = Utf8ToOneUcs4Char((uint8_t *)src, n);
        3697            1032 :             if (v >= 0x10000 && !useCESU8) {
        3698               0 :                 v -= 0x10000;
        3699               0 :                 if (v > 0xFFFFF || dstlen < 2) {
        3700               0 :                     *dstlenp = (origDstlen - dstlen);
        3701               0 :                     if (cx) {
        3702                 :                         char buffer[10];
        3703               0 :                         JS_snprintf(buffer, 10, "0x%x", v + 0x10000);
        3704                 :                         JS_ReportErrorFlagsAndNumber(cx,
        3705                 :                                                      JSREPORT_ERROR,
        3706                 :                                                      js_GetErrorMessage, NULL,
        3707                 :                                                      JSMSG_UTF8_CHAR_TOO_LARGE,
        3708               0 :                                                      buffer);
        3709                 :                     }
        3710               0 :                     return JS_FALSE;
        3711                 :                 }
        3712               0 :                 if (dst) {
        3713               0 :                     *dst++ = (jschar)((v >> 10) + 0xD800);
        3714               0 :                     v = (jschar)((v & 0x3FF) + 0xDC00);
        3715                 :                 }
        3716               0 :                 dstlen--;
        3717                 :             }
        3718                 :         }
        3719       341152518 :         if (!dstlen)
        3720               0 :             goto bufferTooSmall;
        3721       341152518 :         if (dst)
        3722       340876355 :             *dst++ = (jschar) v;
        3723       341152518 :         dstlen--;
        3724       341152518 :         offset += n;
        3725       341152518 :         src += n;
        3726       341152518 :         srclen -= n;
        3727                 :     }
        3728          122321 :     *dstlenp = (origDstlen - dstlen);
        3729          122321 :     return JS_TRUE;
        3730                 :
        3731                 : badCharacter:
        3732               1 :     *dstlenp = (origDstlen - dstlen);
        3733               1 :     if (cx) {
        3734                 :         char buffer[10];
        3735               1 :         JS_snprintf(buffer, 10, "%d", offset);
        3736                 :         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
        3737                 :                                      js_GetErrorMessage, NULL,
        3738                 :                                      JSMSG_MALFORMED_UTF8_CHAR,
        3739               1 :                                      buffer);
        3740                 :     }
        3741               1 :     return JS_FALSE;
        3742                 :
        3743                 : bufferTooSmall:
        3744               0 :     *dstlenp = (origDstlen - dstlen);
        3745               0 :     if (cx) {
        3746                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3747               0 :                              JSMSG_BUFFER_TOO_SMALL);
        3748                 :     }
        3749               0 :     return JS_FALSE;
        3750                 : }
        3751                 :
        3752                 : } /* namepsace js */
        3753                 :
        3754                 : const jschar js_uriReservedPlusPound_ucstr[] =
        3755                 :     {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};
        3756                 : const jschar js_uriUnescaped_ucstr[] =
        3757                 :     {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        3758                 :      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        3759                 :      'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        3760                 :      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
        3761                 :      'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        3762                 :      '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
        3763                 :
        3764                 : #define ____ false
        3765                 :
        3766                 : /*
        3767                 :  * Identifier start chars:
        3768                 :  * -      36:    $
        3769                 :  * -  65..90: A..Z
        3770                 :  * -      95:    _
        3771                 :  * - 97..122: a..z
        3772                 :  */
        3773                 : const bool js_isidstart[] = {
        3774                 : /*       0     1     2     3     4     5     6     7     8     9  */
        3775                 : /*  0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3776                 : /*  1 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3777                 : /*  2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3778                 : /*  3 */ ____, ____, ____, ____, ____, ____, true, ____, ____, ____,
        3779                 : /*  4 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3780                 : /*  5 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3781                 : /*  6 */ ____, ____, ____, ____, ____, true, true, true, true, true,
        3782                 : /*  7 */ true, true, true, true, true, true, true, true, true, true,
        3783                 : /*  8 */ true, true, true, true, true, true, true, true, true, true,
        3784                 : /*  9 */ true, ____, ____, ____, ____, true, ____, true, true, true,
        3785                 : /* 10 */ true, true, true, true, true, true, true, true, true, true,
        3786                 : /* 11 */ true, true, true, true, true, true, true, true, true, true,
        3787                 : /* 12 */ true, true, true, ____, ____, ____, ____, ____
        3788                 : };
        3789                 :
        3790                 : /*
        3791                 :  * Identifier chars:
        3792                 :  * -      36:    $
        3793                 :  * -  48..57: 0..9
        3794                 :  * -  65..90: A..Z
        3795                 :  * -      95:    _
        3796                 :  * - 97..122: a..z
        3797                 :  */
        3798                 : const bool js_isident[] = {
        3799                 : /*       0     1     2     3     4     5     6     7     8     9  */
        3800                 : /*  0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3801                 : /*  1 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3802                 : /*  2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3803                 : /*  3 */ ____, ____, ____, ____, ____, ____, true, ____, ____, ____,
        3804                 : /*  4 */ ____, ____, ____, ____, ____, ____, ____, ____, true, true,
        3805                 : /*  5 */ true, true, true, true, true, true, true, true, ____, ____,
        3806                 : /*  6 */ ____, ____, ____, ____, ____, true, true, true, true, true,
        3807                 : /*  7 */ true, true, true, true, true, true, true, true, true, true,
        3808                 : /*  8 */ true, true, true, true, true, true, true, true, true, true,
        3809                 : /*  9 */ true, ____, ____, ____, ____, true, ____, true, true, true,
        3810                 : /* 10 */ true, true, true, true, true, true, true, true, true, true,
        3811                 : /* 11 */ true, true, true, true, true, true, true, true, true, true,
        3812                 : /* 12 */ true, true, true, ____, ____, ____, ____, ____
        3813                 : };
        3814                 :
        3815                 : /* Whitespace chars: '\t', '\n', '\v', '\f', '\r', ' '. */
        3816                 : const bool js_isspace[] = {
        3817                 : /*       0     1     2     3     4     5     6     7     8     9  */
        3818                 : /*  0 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, true,
        3819                 : /*  1 */ true, true, true, true, ____, ____, ____, ____, ____, ____,
        3820                 : /*  2 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3821                 : /*  3 */ ____, ____, true, ____, ____, ____, ____, ____, ____, ____,
        3822                 : /*  4 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3823                 : /*  5 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3824                 : /*  6 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3825                 : /*  7 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3826                 : /*  8 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3827                 : /*  9 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3828                 : /* 10 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3829                 : /* 11 */ ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
        3830                 : /* 12 */ ____, ____, ____, ____, ____, ____, ____, ____
        3831                 : };
        3832                 :
        3833                 : #undef ____
        3834                 :
        3835                 : #define URI_CHUNK 64U
        3836                 :
        3837                 : static inline bool
        3838            3065 : TransferBufferToString(JSContext *cx, StringBuffer &sb, Value *rval)
        3839                 : {
        3840            3065 :     JSString *str = sb.finishString();
        3841            3065 :     if (!str)
        3842               0 :         return false;
        3843            3065 :     rval->setString(str);
        3844            3065 :     return true;
        3845                 : }
        3846                 :
        3847                 : /*
        3848                 :  * ECMA 3, 15.1.3 URI Handling Function Properties
        3849                 :  *
        3850                 :  * The following are implementations of the algorithms
        3851                 :  * given in the ECMA specification for the hidden functions
        3852                 :  * 'Encode' and 'Decode'.
        3853                 :  */
        3854                 : static JSBool
        3855            1547 : Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
        3856                 :        const jschar *unescapedSet2, Value *rval)
        3857                 : {
        3858                 :     static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
        3859                 :
        3860            1547 :     size_t length = str->length();
        3861            1547 :     const jschar *chars = str->getChars(cx);
        3862            1547 :     if (!chars)
        3863               0 :         return JS_FALSE;
        3864                 :
        3865            1547 :     if (length == 0) {
        3866             100 :         rval->setString(cx->runtime->emptyString);
        3867             100 :         return JS_TRUE;
        3868                 :     }
        3869                 :
        3870            2894 :     StringBuffer sb(cx);
        3871                 :     jschar hexBuf[4];
        3872            1447 :     hexBuf[0] = '%';
        3873            1447 :     hexBuf[3] = 0;
        3874           28507 :     for (size_t k = 0; k < length; k++) {
        3875           27060 :         jschar c = chars[k];
        3876           27104 :         if (js_strchr(unescapedSet, c) ||
        3877              44 :             (unescapedSet2 && js_strchr(unescapedSet2, c))) {
        3878           24910 :             if (!sb.append(c))
        3879               0 :                 return JS_FALSE;
        3880                 :         } else {
        3881            2150 :             if ((c >= 0xDC00) && (c <= 0xDFFF)) {
        3882                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3883               0 :                                  JSMSG_BAD_URI, NULL);
        3884               0 :                 return JS_FALSE;
        3885                 :             }
        3886                 :             uint32_t v;
        3887            2150 :             if (c < 0xD800 || c > 0xDBFF) {
        3888            2150 :                 v = c;
        3889                 :             } else {
        3890               0 :                 k++;
        3891               0 :                 if (k == length) {
        3892                 :                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3893               0 :                                      JSMSG_BAD_URI, NULL);
        3894               0 :                     return JS_FALSE;
        3895                 :                 }
        3896               0 :                 jschar c2 = chars[k];
        3897               0 :                 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
        3898                 :                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        3899               0 :                                      JSMSG_BAD_URI, NULL);
        3900               0 :                     return JS_FALSE;
        3901                 :                 }
        3902               0 :                 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
        3903                 :             }
        3904                 :             uint8_t utf8buf[4];
        3905            2150 :             size_t L = js_OneUcs4ToUtf8Char(utf8buf, v);
        3906            4426 :             for (size_t j = 0; j < L; j++) {
        3907            2276 :                 hexBuf[1] = HexDigits[utf8buf[j] >> 4];
        3908            2276 :                 hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
        3909            2276 :                 if (!sb.append(hexBuf, 3))
        3910               0 :                     return JS_FALSE;
        3911                 :             }
        3912                 :         }
        3913                 :     }
        3914                 :
        3915            1447 :     return TransferBufferToString(cx, sb, rval);
        3916                 : }
        3917                 :
        3918                 : static JSBool
        3919            1632 : Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval)
        3920                 : {
        3921            1632 :     size_t length = str->length();
        3922            1632 :     const jschar *chars = str->getChars(cx);
        3923            1632 :     if (!chars)
        3924               0 :         return JS_FALSE;
        3925                 :
        3926            1632 :     if (length == 0) {
        3927               3 :         rval->setString(cx->runtime->emptyString);
        3928               3 :         return JS_TRUE;
        3929                 :     }
        3930                 :
        3931            3258 :     StringBuffer sb(cx);
        3932           27849 :     for (size_t k = 0; k < length; k++) {
        3933           26231 :         jschar c = chars[k];
        3934           26231 :         if (c == '%') {
        3935              37 :             size_t start = k;
        3936              37 :             if ((k + 2) >= length)
        3937               9 :                 goto report_bad_uri;
        3938              28 :             if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
        3939                 :                 goto report_bad_uri;
        3940              26 :             uint32_t B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
        3941              26 :             k += 2;
        3942              26 :             if (!(B & 0x80)) {
        3943              26 :                 c = (jschar)B;
        3944                 :             } else {
        3945               0 :                 int n = 1;
        3946               0 :                 while (B & (0x80 >> n))
        3947               0 :                     n++;
        3948               0 :                 if (n == 1 || n > 4)
        3949                 :                     goto report_bad_uri;
        3950                 :                 uint8_t octets[4];
        3951               0 :                 octets[0] = (uint8_t)B;
        3952               0 :                 if (k + 3 * (n - 1) >= length)
        3953               0 :                     goto report_bad_uri;
        3954               0 :                 for (int j = 1; j < n; j++) {
        3955               0 :                     k++;
        3956               0 :                     if (chars[k] != '%')
        3957               0 :                         goto report_bad_uri;
        3958               0 :                     if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
        3959                 :                         goto report_bad_uri;
        3960               0 :                     B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
        3961               0 :                     if ((B & 0xC0) != 0x80)
        3962               0 :                         goto report_bad_uri;
        3963               0 :                     k += 2;
        3964               0 :                     octets[j] = (char)B;
        3965                 :                 }
        3966               0 :                 uint32_t v = Utf8ToOneUcs4Char(octets, n);
        3967               0 :                 if (v >= 0x10000) {
        3968               0 :                     v -= 0x10000;
        3969               0 :                     if (v > 0xFFFFF)
        3970               0 :                         goto report_bad_uri;
        3971               0 :                     c = (jschar)((v & 0x3FF) + 0xDC00);
        3972               0 :                     jschar H = (jschar)((v >> 10) + 0xD800);
        3973               0 :                     if (!sb.append(H))
        3974               0 :                         return JS_FALSE;
        3975                 :                 } else {
        3976               0 :                     c = (jschar)v;
        3977                 :                 }
        3978                 :             }
        3979              26 :             if (js_strchr(reservedSet, c)) {
        3980               1 :                 if (!sb.append(chars + start, k - start + 1))
        3981               0 :                     return JS_FALSE;
        3982                 :             } else {
        3983              25 :                 if (!sb.append(c))
        3984               0 :                     return JS_FALSE;
        3985                 :             }
        3986                 :         } else {
        3987           26194 :             if (!sb.append(c))
        3988               0 :                 return JS_FALSE;
        3989                 :         }
        3990                 :     }
        3991                 :
        3992            1618 :     return TransferBufferToString(cx, sb, rval);
        3993                 :
        3994                 :   report_bad_uri:
        3995              11 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
        3996                 :     /* FALL THROUGH */
        3997                 :
        3998              11 :     return JS_FALSE;
        3999                 : }
        4000                 :
        4001                 : static JSBool
        4002            1144 : str_decodeURI(JSContext *cx, unsigned argc, Value *vp)
        4003                 : {
        4004            1144 :     CallArgs args = CallArgsFromVp(argc, vp);
        4005            1144 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4006            1144 :     if (!str)
        4007               0 :         return false;
        4008                 :
        4009                 :     Value result;
        4010            1144 :     if (!Decode(cx, str, js_uriReservedPlusPound_ucstr, &result))
        4011               9 :         return false;
        4012                 :
        4013            1135 :     args.rval() = result;
        4014            1135 :     return true;
        4015                 : }
        4016                 :
        4017                 : static JSBool
        4018             488 : str_decodeURI_Component(JSContext *cx, unsigned argc, Value *vp)
        4019                 : {
        4020             488 :     CallArgs args = CallArgsFromVp(argc, vp);
        4021             488 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4022             488 :     if (!str)
        4023               0 :         return false;
        4024                 :
        4025                 :     Value result;
        4026             488 :     if (!Decode(cx, str, js_empty_ucstr, &result))
        4027               2 :         return false;
        4028                 :
        4029             486 :     args.rval() = result;
        4030             486 :     return true;
        4031                 : }
        4032                 :
        4033                 : static JSBool
        4034               1 : str_encodeURI(JSContext *cx, unsigned argc, Value *vp)
        4035                 : {
        4036               1 :     CallArgs args = CallArgsFromVp(argc, vp);
        4037               1 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4038               1 :     if (!str)
        4039               0 :         return false;
        4040                 :
        4041                 :     Value result;
        4042               1 :     if (!Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, &result))
        4043               0 :         return false;
        4044                 :
        4045               1 :     args.rval() = result;
        4046               1 :     return true;
        4047                 : }
        4048                 :
        4049                 : static JSBool
        4050            1546 : str_encodeURI_Component(JSContext *cx, unsigned argc, Value *vp)
        4051                 : {
        4052            1546 :     CallArgs args = CallArgsFromVp(argc, vp);
        4053            1546 :     JSLinearString *str = ArgToRootedString(cx, args, 0);
        4054            1546 :     if (!str)
        4055               0 :         return false;
        4056                 :
        4057                 :     Value result;
        4058            1546 :     if (!Encode(cx, str, js_uriUnescaped_ucstr, NULL, &result))
        4059               0 :         return false;
        4060                 :
        4061            1546 :     args.rval() = result;
        4062            1546 :     return true;
        4063                 : }
        4064                 :
        4065                 : /*
        4066                 :  * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
        4067                 :  * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
        4068                 :  */
        4069                 : int
        4070            2162 : js_OneUcs4ToUtf8Char(uint8_t *utf8Buffer, uint32_t ucs4Char)
        4071                 : {
        4072            2162 :     int utf8Length = 1;
        4073                 :
        4074            2162 :     JS_ASSERT(ucs4Char <= 0x10FFFF);
        4075            2162 :     if (ucs4Char < 0x80) {
        4076            2087 :         *utf8Buffer = (uint8_t)ucs4Char;
        4077                 :     } else {
        4078                 :         int i;
        4079              75 :         uint32_t a = ucs4Char >> 11;
        4080              75 :         utf8Length = 2;
        4081             215 :         while (a) {
        4082              65 :             a >>= 5;
        4083              65 :             utf8Length++;
        4084                 :         }
        4085              75 :         i = utf8Length;
        4086             290 :         while (--i) {
        4087             140 :             utf8Buffer[i] = (uint8_t)((ucs4Char & 0x3F) | 0x80);
        4088             140 :             ucs4Char >>= 6;
        4089                 :         }
        4090              75 :         *utf8Buffer = (uint8_t)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
        4091                 :     }
        4092            2162 :     return utf8Length;
        4093                 : }
        4094                 :
        4095                 : /*
        4096                 :  * Convert a utf8 character sequence into a UCS-4 character and return that
        4097                 :  * character.  It is assumed that the caller already checked that the sequence
        4098                 :  * is valid.
        4099                 :  */
        4100                 : static uint32_t
        4101            1032 : Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length)
        4102                 : {
        4103            1032 :     JS_ASSERT(1 <= utf8Length && utf8Length <= 4);
        4104                 :
        4105            1032 :     if (utf8Length == 1) {
        4106               0 :         JS_ASSERT(!(*utf8Buffer & 0x80));
        4107               0 :         return *utf8Buffer;
        4108                 :     }
        4109                 :
        4110                 :     /* from Unicode 3.1, non-shortest form is illegal */
        4111                 :     static const uint32_t minucs4Table[] = { 0x80, 0x800, 0x10000 };
        4112                 :
        4113               0 :     JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7 - utf8Length)))) ==
        4114            1032 :               (0x100 - (1 << (8 - utf8Length))));
        4115            1032 :     uint32_t ucs4Char = *utf8Buffer++ & ((1 << (7 - utf8Length)) - 1);
        4116            1032 :     uint32_t minucs4Char = minucs4Table[utf8Length - 2];
        4117            3876 :     while (--utf8Length) {
        4118            1812 :         JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
        4119            1812 :         ucs4Char = (ucs4Char << 6) | (*utf8Buffer++ & 0x3F);
        4120                 :     }
        4121                 :
        4122            1032 :     if (JS_UNLIKELY(ucs4Char < minucs4Char || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF)))
        4123               0 :         return INVALID_UTF8;
        4124                 :
        4125            1032 :     return ucs4Char;
        4126                 : }
        4127                 :
        4128                 : namespace js {
        4129                 :
        4130                 : size_t
        4131          436519 : PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32_t quote)
        4132                 : {
        4133                 :     enum {
        4134                 :         STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
        4135                 :     } state;
        4136                 :
        4137          436519 :     JS_ASSERT(quote == 0 || quote == '\'' || quote == '"');
        4138          436519 :     JS_ASSERT_IF(!buffer, bufferSize == 0);
        4139          436519 :     JS_ASSERT_IF(fp, !buffer);
        4140                 :
        4141          436519 :     if (bufferSize == 0)
        4142               0 :         buffer = NULL;
        4143                 :     else
        4144          436519 :         bufferSize--;
        4145                 :
        4146          436519 :     const jschar *chars = str->chars();
        4147          436519 :     const jschar *charsEnd = chars + str->length();
        4148          436519 :     size_t n = 0;
        4149          436519 :     state = FIRST_QUOTE;
        4150          436519 :     unsigned shift = 0;
        4151          436519 :     unsigned hex = 0;
        4152          436519 :     unsigned u = 0;
        4153          436519 :     char c = 0;  /* to quell GCC warnings */
        4154                 :
        4155         4380607 :     for (;;) {
        4156         4817126 :         switch (state) {
        4157                 :           case STOP:
        4158          436519 :             goto stop;
        4159                 :           case FIRST_QUOTE:
        4160          436519 :             state = CHARS;
        4161          436519 :             goto do_quote;
        4162                 :           case LAST_QUOTE:
        4163          436519 :             state = STOP;
        4164                 :           do_quote:
        4165          873038 :             if (quote == 0)
        4166          873038 :                 continue;
        4167               0 :             c = (char)quote;
        4168               0 :             break;
        4169                 :           case CHARS:
        4170         3506273 :             if (chars == charsEnd) {
        4171          436519 :                 state = LAST_QUOTE;
        4172          436519 :                 continue;
        4173                 :             }
        4174         3069754 :             u = *chars++;
        4175         3069754 :             if (u < ' ') {
        4176             360 :                 if (u != 0) {
        4177             350 :                     const char *escape = strchr(js_EscapeMap, (int)u);
        4178             350 :                     if (escape) {
        4179             100 :                         u = escape[1];
        4180             100 :                         goto do_escape;
        4181                 :                     }
        4182                 :                 }
        4183             260 :                 goto do_hex_escape;
        4184                 :             }
        4185         3069394 :             if (u < 127) {
        4186         3069384 :                 if (u == quote || u == '\\')
        4187                 :                     goto do_escape;
        4188         3069376 :                 c = (char)u;
        4189              10 :             } else if (u < 0x100) {
        4190              10 :                 goto do_hex_escape;
        4191                 :             } else {
        4192               0 :                 shift = 16;
        4193               0 :                 hex = u;
        4194               0 :                 u = 'u';
        4195               0 :                 goto do_escape;
        4196                 :             }
        4197         3069376 :             break;
        4198                 :           do_hex_escape:
        4199             270 :             shift = 8;
        4200             270 :             hex = u;
        4201             270 :             u = 'x';
        4202                 :           do_escape:
        4203             378 :             c = '\\';
        4204             378 :             state = ESCAPE_START;
        4205             378 :             break;
        4206                 :           case ESCAPE_START:
        4207             378 :             JS_ASSERT(' ' <= u && u < 127);
        4208             378 :             c = (char)u;
        4209             378 :             state = ESCAPE_MORE;
        4210             378 :             break;
        4211                 :           case ESCAPE_MORE:
        4212             918 :             if (shift == 0) {
        4213             378 :                 state = CHARS;
        4214             378 :                 continue;
        4215                 :             }
        4216             540 :             shift -= 4;
        4217             540 :             u = 0xF & (hex >> shift);
        4218             540 :             c = (char)(u + (u < 10 ? '0' : 'A' - 10));
        4219             540 :             break;
        4220                 :         }
        4221         3070672 :         if (buffer) {
        4222         3070672 :             JS_ASSERT(n <= bufferSize);
        4223         3070672 :             if (n != bufferSize) {
        4224         3070672 :                 buffer[n] = c;
        4225                 :             } else {
        4226               0 :                 buffer[n] = '\0';
        4227               0 :                 buffer = NULL;
        4228                 :             }
        4229               0 :         } else if (fp) {
        4230               0 :             if (fputc(c, fp) < 0)
        4231               0 :                 return size_t(-1);
        4232                 :         }
        4233         3070672 :         n++;
        4234                 :     }
        4235                 :   stop:
        4236          436519 :     if (buffer)
        4237          436519 :         buffer[n] = '\0';
        4238          436519 :     return n;
        4239                 : }
        4240                 :
        4241                 : } /* namespace js */

Generated by: LCOV version 1.7