LCOV - code coverage report
Current view: directory - js/src - jsstr.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 2118 1507 71.2 %
Date: 2012-06-02 Functions: 137 114 83.2 %

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

Generated by: LCOV version 1.7