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

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

Generated by: LCOV version 1.7