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

Diff of /trunk/js/jsstr.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC
# Line 1  Line 1 
1  /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-  /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2   * vim: set ts=8 sw=4 et tw=99:   * vim: set ts=8 sw=4 et tw=99:
3   *   *
4   * ***** BEGIN LICENSE BLOCK *****   * ***** BEGIN LICENSE BLOCK *****
# Line 48  Line 48 
48   * of rooting things that might lose their newborn root due to subsequent GC   * of rooting things that might lose their newborn root due to subsequent GC
49   * allocations in the same native method.   * allocations in the same native method.
50   */   */
51  #include "jsstddef.h"  #define __STDC_LIMIT_MACROS
52    
53  #include <stdlib.h>  #include <stdlib.h>
54  #include <string.h>  #include <string.h>
55  #include "jstypes.h"  #include "jstypes.h"
56    #include "jsstdint.h"
57  #include "jsutil.h" /* Added by JSIFY */  #include "jsutil.h" /* Added by JSIFY */
58  #include "jshash.h" /* Added by JSIFY */  #include "jshash.h" /* Added by JSIFY */
59  #include "jsprf.h"  #include "jsprf.h"
# Line 73  Line 75 
75  #include "jsstaticcheck.h"  #include "jsstaticcheck.h"
76  #include "jsstr.h"  #include "jsstr.h"
77  #include "jsbit.h"  #include "jsbit.h"
78    #include "jsvector.h"
79    #include "jsstrinlines.h"
80    
81  #define JSSTRDEP_RECURSION_LIMIT        100  #define JSSTRDEP_RECURSION_LIMIT        100
82    
83  size_t  JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
84  js_MinimizeDependentStrings(JSString *str, int level, JSString **basep)  JS_STATIC_ASSERT(INT_FITS_IN_JSVAL(JSString::MAX_LENGTH));
85    
86    static size_t
87    MinimizeDependentStrings(JSString *str, int level, JSString **basep)
88  {  {
89      JSString *base;      JSString *base;
90      size_t start, length;      size_t start, length;
91    
92      JS_ASSERT(JSSTRING_IS_DEPENDENT(str));      JS_ASSERT(str->isDependent());
93      base = JSSTRDEP_BASE(str);      base = str->dependentBase();
94      start = JSSTRDEP_START(str);      start = str->dependentStart();
95      if (JSSTRING_IS_DEPENDENT(base)) {      if (base->isDependent()) {
96          if (level < JSSTRDEP_RECURSION_LIMIT) {          if (level < JSSTRDEP_RECURSION_LIMIT) {
97              start += js_MinimizeDependentStrings(base, level + 1, &base);              start += MinimizeDependentStrings(base, level + 1, &base);
98          } else {          } else {
99              do {              do {
100                  start += JSSTRDEP_START(base);                  start += base->dependentStart();
101                  base = JSSTRDEP_BASE(base);                  base = base->dependentBase();
102              } while (JSSTRING_IS_DEPENDENT(base));              } while (base->isDependent());
103          }          }
104          if (start == 0) {          if (start == 0) {
105              JS_ASSERT(JSSTRDEP_IS_PREFIX(str));              JS_ASSERT(str->dependentIsPrefix());
106              JSPREFIX_SET_BASE(str, base);              str->prefixSetBase(base);
107          } else if (start <= JSSTRDEP_START_MASK) {          } else if (start <= JSString::MAX_DEPENDENT_START) {
108              length = JSSTRDEP_LENGTH(str);              length = str->dependentLength();
109              JSSTRDEP_REINIT(str, base, start, length);              str->reinitDependent(base, start, length);
110          }          }
111      }      }
112      *basep = base;      *basep = base;
# Line 112  Line 119 
119      size_t start;      size_t start;
120      JSString *base;      JSString *base;
121    
122      start = js_MinimizeDependentStrings(str, 0, &base);      start = MinimizeDependentStrings(str, 0, &base);
123      JS_ASSERT(start < JSFLATSTR_LENGTH(base));      JS_ASSERT(start < base->flatLength());
124      return JSFLATSTR_CHARS(base) + start;      return base->flatChars() + start;
125  }  }
126    
127  const jschar *  const jschar *
# Line 122  Line 129 
129  {  {
130      if (!js_MakeStringImmutable(cx, str))      if (!js_MakeStringImmutable(cx, str))
131          return NULL;          return NULL;
132      return JSFLATSTR_CHARS(str);      return str->flatChars();
133  }  }
134    
135  JSString * JS_FASTCALL  JSString * JS_FASTCALL
136  js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)  js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
137  {  {
138      size_t rn, ln, lrdist, n;      size_t rn, ln, lrdist, n;
139      jschar *rs, *ls, *s;      jschar *ls, *s;
140        const jschar *rs;
141      JSString *ldep;             /* non-null if left should become dependent */      JSString *ldep;             /* non-null if left should become dependent */
142      JSString *str;      JSString *str;
143    
144      JSSTRING_CHARS_AND_LENGTH(right, rs, rn);      right->getCharsAndLength(rs, rn);
145      if (rn == 0)      if (rn == 0)
146          return left;          return left;
147    
148      JSSTRING_CHARS_AND_LENGTH(left, ls, ln);      left->getCharsAndLength(const_cast<const jschar *&>(ls), ln);
149      if (ln == 0)      if (ln == 0)
150          return right;          return right;
151    
152      if (!JSSTRING_IS_MUTABLE(left)) {      if (!left->isMutable()) {
153          /* We must copy if left does not own a buffer to realloc. */          /* We must copy if left does not own a buffer to realloc. */
154          s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar));          s = (jschar *) cx->malloc((ln + rn + 1) * sizeof(jschar));
155          if (!s)          if (!s)
156              return NULL;              return NULL;
157          js_strncpy(s, ls, ln);          js_strncpy(s, ls, ln);
158          ldep = NULL;          ldep = NULL;
159      } else {      } else {
160          /* We can realloc left's space and make it depend on our result. */          /* We can realloc left's space and make it depend on our result. */
161          JS_ASSERT(JSSTRING_IS_FLAT(left));          JS_ASSERT(left->isFlat());
162          s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar));          s = (jschar *) cx->realloc(ls, (ln + rn + 1) * sizeof(jschar));
163          if (!s)          if (!s)
164              return NULL;              return NULL;
165    
# Line 159  Line 167 
167          lrdist = (size_t)(rs - ls);          lrdist = (size_t)(rs - ls);
168          if (lrdist < ln)          if (lrdist < ln)
169              rs = s + lrdist;              rs = s + lrdist;
170          left->u.chars = ls = s;          left->mChars = ls = s;
171          ldep = left;          ldep = left;
172      }      }
173    
# Line 171  Line 179 
179      if (!str) {      if (!str) {
180          /* Out of memory: clean up any space we (re-)allocated. */          /* Out of memory: clean up any space we (re-)allocated. */
181          if (!ldep) {          if (!ldep) {
182              JS_free(cx, s);              cx->free(s);
183          } else {          } else {
184              s = (jschar *) JS_realloc(cx, ls, (ln + 1) * sizeof(jschar));              s = (jschar *) cx->realloc(ls, (ln + 1) * sizeof(jschar));
185              if (s)              if (s)
186                  left->u.chars = s;                  left->mChars = s;
187          }          }
188      } else {      } else {
189          JSFLATSTR_SET_MUTABLE(str);          str->flatSetMutable();
190    
191          /* Morph left into a dependent prefix if we realloc'd its buffer. */          /* Morph left into a dependent prefix if we realloc'd its buffer. */
192          if (ldep) {          if (ldep) {
193              JSPREFIX_REINIT(ldep, str, ln);              ldep->reinitPrefix(str, ln);
194  #ifdef DEBUG  #ifdef DEBUG
195            {              {
196              JSRuntime *rt = cx->runtime;                  JSRuntime *rt = cx->runtime;
197              JS_RUNTIME_METER(rt, liveDependentStrings);                  JS_RUNTIME_METER(rt, liveDependentStrings);
198              JS_RUNTIME_METER(rt, totalDependentStrings);                  JS_RUNTIME_METER(rt, totalDependentStrings);
199              JS_LOCK_RUNTIME_VOID(rt,                  JS_LOCK_RUNTIME_VOID(rt,
200                  (rt->strdepLengthSum += (double)ln,                      (rt->strdepLengthSum += (double)ln,
201                   rt->strdepLengthSquaredSum += (double)ln * (double)ln));                       rt->strdepLengthSquaredSum += (double)ln * (double)ln));
202            }              }
203  #endif  #endif
204          }          }
205      }      }
# Line 205  Line 213 
213      size_t n, size;      size_t n, size;
214      jschar *s;      jschar *s;
215    
216      if (JSSTRING_IS_DEPENDENT(str)) {      if (str->isDependent()) {
217          n = JSSTRDEP_LENGTH(str);          n = str->dependentLength();
218          size = (n + 1) * sizeof(jschar);          size = (n + 1) * sizeof(jschar);
219          s = (jschar *) JS_malloc(cx, size);          s = (jschar *) cx->malloc(size);
220          if (!s)          if (!s)
221              return NULL;              return NULL;
222    
223          js_strncpy(s, JSSTRDEP_CHARS(str), n);          js_strncpy(s, str->dependentChars(), n);
224          s[n] = 0;          s[n] = 0;
225          JSFLATSTR_REINIT(str, s, n);          str->reinitFlat(s, n);
226    
227  #ifdef DEBUG  #ifdef DEBUG
228          {          {
# Line 228  Line 236 
236  #endif  #endif
237      }      }
238    
239      return JSFLATSTR_CHARS(str);      return str->flatChars();
240  }  }
241    
242  JSBool  JSBool
243  js_MakeStringImmutable(JSContext *cx, JSString *str)  js_MakeStringImmutable(JSContext *cx, JSString *str)
244  {  {
245      if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(cx, str)) {      if (str->isDependent() && !js_UndependString(cx, str)) {
246          JS_RUNTIME_METER(cx->runtime, badUndependStrings);          JS_RUNTIME_METER(cx->runtime, badUndependStrings);
247          return JS_FALSE;          return JS_FALSE;
248      }      }
249      JSFLATSTR_CLEAR_MUTABLE(str);      str->flatClearMutable();
250      return JS_TRUE;      return JS_TRUE;
251  }  }
252    
253  static JSString *  static JSString *
254  ArgToRootedString(JSContext *cx, uintN argc, jsval *vp, uintN arg)  ArgToRootedString(JSContext *cx, uintN argc, jsval *vp, uintN arg)
255  {  {
     JSObject *obj;  
     JSString *str;  
   
256      if (arg >= argc)      if (arg >= argc)
257          return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);          return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
258      vp += 2 + arg;      vp += 2 + arg;
259    
260      if (JSVAL_IS_OBJECT(*vp)) {      if (!JSVAL_IS_PRIMITIVE(*vp) && !JSVAL_TO_OBJECT(*vp)->defaultValue(cx, JSTYPE_STRING, vp))
261          obj = JSVAL_TO_OBJECT(*vp);          return NULL;
262          if (!obj)  
263              return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);      JSString *str;
264          if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, vp))      if (JSVAL_IS_STRING(*vp)) {
265              return NULL;          str = JSVAL_TO_STRING(*vp);
     }  
     if (JSVAL_IS_STRING(*vp))  
         return JSVAL_TO_STRING(*vp);  
     if (JSVAL_IS_INT(*vp)) {  
         str = js_NumberToString(cx, JSVAL_TO_INT(*vp));  
     } else if (JSVAL_IS_DOUBLE(*vp)) {  
         str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(*vp));  
266      } else if (JSVAL_IS_BOOLEAN(*vp)) {      } else if (JSVAL_IS_BOOLEAN(*vp)) {
267          return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[          str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
268                                    JSVAL_TO_BOOLEAN(*vp)? 1 : 0]);                                    JSVAL_TO_BOOLEAN(*vp)? 1 : 0]);
269      } else {      } else if (JSVAL_IS_NULL(*vp)) {
270          JS_ASSERT(JSVAL_IS_VOID(*vp));          str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
271          return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);      } else if (JSVAL_IS_VOID(*vp)) {
272            str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
273        }
274        else {
275            if (JSVAL_IS_INT(*vp)) {
276                str = js_NumberToString(cx, JSVAL_TO_INT(*vp));
277            } else {
278                JS_ASSERT(JSVAL_IS_DOUBLE(*vp));
279                str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(*vp));
280            }
281            if (str)
282                *vp = STRING_TO_JSVAL(str);
283      }      }
     if (str)  
         *vp = STRING_TO_JSVAL(str);  
284      return str;      return str;
285  }  }
286    
# Line 292  Line 299 
299  static JSBool  static JSBool
300  str_encodeURI_Component(JSContext *cx, uintN argc, jsval *vp);  str_encodeURI_Component(JSContext *cx, uintN argc, jsval *vp);
301    
302    static const uint32 OVERLONG_UTF8 = UINT32_MAX;
303    
304  static uint32  static uint32
305  Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);  Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
306    
# Line 369  Line 378 
378      if (!str)      if (!str)
379          return JS_FALSE;          return JS_FALSE;
380    
381      JSSTRING_CHARS_AND_LENGTH(str, chars, length);      str->getCharsAndLength(chars, length);
382      newlength = length;      newlength = length;
383    
384      /* Take a first pass and see how big the result string will need to be. */      /* Take a first pass and see how big the result string will need to be. */
# Line 399  Line 408 
408          return JS_FALSE;          return JS_FALSE;
409      }      }
410    
411      newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));      newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
412      if (!newchars)      if (!newchars)
413          return JS_FALSE;          return JS_FALSE;
414      for (i = 0, ni = 0; i < length; i++) {      for (i = 0, ni = 0; i < length; i++) {
# Line 427  Line 436 
436    
437      str = js_NewString(cx, newchars, newlength);      str = js_NewString(cx, newchars, newlength);
438      if (!str) {      if (!str) {
439          JS_free(cx, newchars);          cx->free(newchars);
440          return JS_FALSE;          return JS_FALSE;
441      }      }
442      *rval = STRING_TO_JSVAL(str);      *rval = STRING_TO_JSVAL(str);
# Line 458  Line 467 
467      if (!str)      if (!str)
468          return JS_FALSE;          return JS_FALSE;
469    
470      JSSTRING_CHARS_AND_LENGTH(str, chars, length);      str->getCharsAndLength(chars, length);
471    
472      /* Don't bother allocating less space for the new string. */      /* Don't bother allocating less space for the new string. */
473      newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));      newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
474      if (!newchars)      if (!newchars)
475          return JS_FALSE;          return JS_FALSE;
476      ni = i = 0;      ni = i = 0;
# Line 490  Line 499 
499    
500      str = js_NewString(cx, newchars, ni);      str = js_NewString(cx, newchars, ni);
501      if (!str) {      if (!str) {
502          JS_free(cx, newchars);          cx->free(newchars);
503          return JS_FALSE;          return JS_FALSE;
504      }      }
505      *vp = STRING_TO_JSVAL(str);      *vp = STRING_TO_JSVAL(str);
# Line 547  Line 556 
556      if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {      if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
557          if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) {          if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) {
558              /* Follow ECMA-262 by fetching intrinsic length of our string. */              /* Follow ECMA-262 by fetching intrinsic length of our string. */
559              v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);              v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
560              JS_ASSERT(JSVAL_IS_STRING(v));              JS_ASSERT(JSVAL_IS_STRING(v));
561              str = JSVAL_TO_STRING(v);              str = JSVAL_TO_STRING(v);
562          } else {          } else {
# Line 557  Line 566 
566                  return JS_FALSE;                  return JS_FALSE;
567          }          }
568    
569          *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str));          *vp = INT_TO_JSVAL((jsint) str->length());
570      }      }
571    
572      return JS_TRUE;      return JS_TRUE;
# Line 572  Line 581 
581      JSString *str, *str1;      JSString *str, *str1;
582      size_t i, length;      size_t i, length;
583    
584      v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);      v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
585      JS_ASSERT(JSVAL_IS_STRING(v));      JS_ASSERT(JSVAL_IS_STRING(v));
586      str = JSVAL_TO_STRING(v);      str = JSVAL_TO_STRING(v);
587    
588      length = JSSTRING_LENGTH(str);      length = str->length();
589      for (i = 0; i < length; i++) {      for (i = 0; i < length; i++) {
590          str1 = js_NewDependentString(cx, str, i, 1);          str1 = js_NewDependentString(cx, str, i, 1);
591          if (!str1)          if (!str1)
592              return JS_FALSE;              return JS_FALSE;
593          if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i),          if (!obj->defineProperty(cx, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL,
594                                   STRING_TO_JSVAL(str1), NULL, NULL,                                   STRING_ELEMENT_ATTRS)) {
                                  STRING_ELEMENT_ATTRS, NULL)) {  
595              return JS_FALSE;              return JS_FALSE;
596          }          }
597      }      }
# Line 601  Line 609 
609      if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING))      if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING))
610          return JS_TRUE;          return JS_TRUE;
611    
612      v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);      v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
613      JS_ASSERT(JSVAL_IS_STRING(v));      JS_ASSERT(JSVAL_IS_STRING(v));
614      str = JSVAL_TO_STRING(v);      str = JSVAL_TO_STRING(v);
615    
616      slot = JSVAL_TO_INT(id);      slot = JSVAL_TO_INT(id);
617      if ((size_t)slot < JSSTRING_LENGTH(str)) {      if ((size_t)slot < str->length()) {
618          str1 = js_GetUnitString(cx, str, (size_t)slot);          str1 = JSString::getUnitString(cx, str, size_t(slot));
619          if (!str1)          if (!str1)
620              return JS_FALSE;              return JS_FALSE;
621          if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot),          if (!obj->defineProperty(cx, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL,
622                                   STRING_TO_JSVAL(str1), NULL, NULL,                                   STRING_ELEMENT_ATTRS)) {
                                  STRING_ELEMENT_ATTRS, NULL)) {  
623              return JS_FALSE;              return JS_FALSE;
624          }          }
625          *objp = obj;          *objp = obj;
# Line 622  Line 629 
629    
630  JSClass js_StringClass = {  JSClass js_StringClass = {
631      js_String_str,      js_String_str,
632      JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |      JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
633      JSCLASS_HAS_CACHED_PROTO(JSProto_String),      JSCLASS_HAS_CACHED_PROTO(JSProto_String),
634      JS_PropertyStub,   JS_PropertyStub,   str_getProperty,   JS_PropertyStub,      JS_PropertyStub,   JS_PropertyStub,   str_getProperty,   JS_PropertyStub,
635      str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub,      str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, NULL,
636      JSCLASS_NO_OPTIONAL_MEMBERS      JSCLASS_NO_OPTIONAL_MEMBERS
637  };  };
638    
# Line 680  Line 687 
687      JSString *str;      JSString *str;
688      size_t i, j, k, n;      size_t i, j, k, n;
689      char buf[16];      char buf[16];
690      jschar *s, *t;      const jschar *s;
691        jschar *t;
692    
693      if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &v))      if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &v))
694          return JS_FALSE;          return JS_FALSE;
# Line 689  Line 697 
697      if (!str)      if (!str)
698          return JS_FALSE;          return JS_FALSE;
699      j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name);      j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name);
700      JSSTRING_CHARS_AND_LENGTH(str, s, k);      str->getCharsAndLength(s, k);
701      n = j + k + 2;      n = j + k + 2;
702      t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));      t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
703      if (!t)      if (!t)
704          return JS_FALSE;          return JS_FALSE;
705      for (i = 0; i < j; i++)      for (i = 0; i < j; i++)
# Line 703  Line 711 
711      t[i] = 0;      t[i] = 0;
712      str = js_NewString(cx, t, n);      str = js_NewString(cx, t, n);
713      if (!str) {      if (!str) {
714          JS_free(cx, t);          cx->free(t);
715          return JS_FALSE;          return JS_FALSE;
716      }      }
717      *vp = STRING_TO_JSVAL(str);      *vp = STRING_TO_JSVAL(str);
# Line 712  Line 720 
720    
721  #endif /* JS_HAS_TOSOURCE */  #endif /* JS_HAS_TOSOURCE */
722    
723  static JSBool  JSBool
724  str_toString(JSContext *cx, uintN argc, jsval *vp)  js_str_toString(JSContext *cx, uintN argc, jsval *vp)
725  {  {
726      return js_GetPrimitiveThis(cx, vp, &js_StringClass, vp);      return js_GetPrimitiveThis(cx, vp, &js_StringClass, vp);
727  }  }
# Line 756  Line 764 
764          d = js_ValueToNumber(cx, &vp[2]);          d = js_ValueToNumber(cx, &vp[2]);
765          if (JSVAL_IS_NULL(vp[2]))          if (JSVAL_IS_NULL(vp[2]))
766              return JS_FALSE;              return JS_FALSE;
767          length = JSSTRING_LENGTH(str);          length = str->length();
768          begin = js_DoubleToInteger(d);          begin = js_DoubleToInteger(d);
769          if (argc == 1) {          if (argc == 1) {
770              end = length;              end = length;
# Line 781  Line 789 
789  {  {
790      if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL))      if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL))
791          return NULL;          return NULL;
792      jsval v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);      jsval v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
793      JS_ASSERT(JSVAL_IS_STRING(v));      JS_ASSERT(JSVAL_IS_STRING(v));
794      return JSVAL_TO_STRING(v);      return JSVAL_TO_STRING(v);
795  }  }
# Line 791  Line 799 
799  js_toLowerCase(JSContext *cx, JSString *str)  js_toLowerCase(JSContext *cx, JSString *str)
800  {  {
801      size_t i, n;      size_t i, n;
802      jschar *s, *news;      const jschar *s;
803        jschar *news;
804    
805      JSSTRING_CHARS_AND_LENGTH(str, s, n);      str->getCharsAndLength(s, n);
806      news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));      news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
807      if (!news)      if (!news)
808          return NULL;          return NULL;
809      for (i = 0; i < n; i++)      for (i = 0; i < n; i++)
# Line 802  Line 811 
811      news[n] = 0;      news[n] = 0;
812      str = js_NewString(cx, news, n);      str = js_NewString(cx, news, n);
813      if (!str) {      if (!str) {
814          JS_free(cx, news);          cx->free(news);
815          return NULL;          return NULL;
816      }      }
817      return str;      return str;
# Line 841  Line 850 
850  js_toUpperCase(JSContext *cx, JSString *str)  js_toUpperCase(JSContext *cx, JSString *str)
851  {  {
852      size_t i, n;      size_t i, n;
853      jschar *s, *news;      const jschar *s;
854        jschar *news;
855    
856      JSSTRING_CHARS_AND_LENGTH(str, s, n);      str->getCharsAndLength(s, n);
857      news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));      news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
858      if (!news)      if (!news)
859          return NULL;          return NULL;
860      for (i = 0; i < n; i++)      for (i = 0; i < n; i++)
# Line 852  Line 862 
862      news[n] = 0;      news[n] = 0;
863      str = js_NewString(cx, news, n);      str = js_NewString(cx, news, n);
864      if (!str) {      if (!str) {
865          JS_free(cx, news);          cx->free(news);
866          return NULL;          return NULL;
867      }      }
868      return str;      return str;
# Line 920  Line 930 
930      if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {      if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {
931          str = JSVAL_TO_STRING(t);          str = JSVAL_TO_STRING(t);
932          i = JSVAL_TO_INT(vp[2]);          i = JSVAL_TO_INT(vp[2]);
933          if ((size_t)i >= JSSTRING_LENGTH(str))          if ((size_t)i >= str->length())
934              goto out_of_range;              goto out_of_range;
935      } else {      } else {
936          str = NormalizeThis(cx, vp);          str = NormalizeThis(cx, vp);
# Line 936  Line 946 
946              d = js_DoubleToInteger(d);              d = js_DoubleToInteger(d);
947          }          }
948    
949          if (d < 0 || JSSTRING_LENGTH(str) <= d)          if (d < 0 || str->length() <= d)
950              goto out_of_range;              goto out_of_range;
951          i = (jsint) d;          i = (jsint) d;
952      }      }
953    
954      str = js_GetUnitString(cx, str, (size_t)i);      str = JSString::getUnitString(cx, str, size_t(i));
955      if (!str)      if (!str)
956          return JS_FALSE;          return JS_FALSE;
957      *vp = STRING_TO_JSVAL(str);      *vp = STRING_TO_JSVAL(str);
# Line 964  Line 974 
974      if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {      if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {
975          str = JSVAL_TO_STRING(t);          str = JSVAL_TO_STRING(t);
976          i = JSVAL_TO_INT(vp[2]);          i = JSVAL_TO_INT(vp[2]);
977          if ((size_t)i >= JSSTRING_LENGTH(str))          if ((size_t)i >= str->length())
978              goto out_of_range;              goto out_of_range;
979      } else {      } else {
980          str = NormalizeThis(cx, vp);          str = NormalizeThis(cx, vp);
# Line 980  Line 990 
990              d = js_DoubleToInteger(d);              d = js_DoubleToInteger(d);
991          }          }
992    
993          if (d < 0 || JSSTRING_LENGTH(str) <= d)          if (d < 0 || str->length() <= d)
994              goto out_of_range;              goto out_of_range;
995          i = (jsint) d;          i = (jsint) d;
996      }      }
997    
998      *vp = INT_TO_JSVAL(JSSTRING_CHARS(str)[i]);      *vp = INT_TO_JSVAL(str->chars()[i]);
999      return JS_TRUE;      return JS_TRUE;
1000    
1001  out_of_range:  out_of_range:
# Line 1000  Line 1010 
1010  js_String_p_charCodeAt(JSString* str, jsdouble d)  js_String_p_charCodeAt(JSString* str, jsdouble d)
1011  {  {
1012      d = js_DoubleToInteger(d);      d = js_DoubleToInteger(d);
1013      if (d < 0 || (int32)JSSTRING_LENGTH(str) <= d)      if (d < 0 || (int32)str->length() <= d)
1014          return js_NaN;          return js_NaN;
1015      return jsdouble(JSSTRING_CHARS(str)[jsuint(d)]);      return jsdouble(str->chars()[jsuint(d)]);
1016  }  }
1017    
1018  int32 FASTCALL  int32 FASTCALL
1019  js_String_p_charCodeAt_int(JSString* str, jsint i)  js_String_p_charCodeAt_int(JSString* str, jsint i)
1020  {  {
1021      if (i < 0 || (int32)JSSTRING_LENGTH(str) <= i)      if (i < 0 || (int32)str->length() <= i)
1022          return 0;          return 0;
1023      return JSSTRING_CHARS(str)[i];      return str->chars()[i];
1024  }  }
1025    JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int,  STRING, INT32, 1, 1)
1026    
1027  jsdouble FASTCALL  jsdouble FASTCALL
1028  js_String_p_charCodeAt0(JSString* str)  js_String_p_charCodeAt0(JSString* str)
1029  {  {
1030      if ((int32)JSSTRING_LENGTH(str) == 0)      if ((int32)str->length() == 0)
1031          return js_NaN;          return js_NaN;
1032      return jsdouble(JSSTRING_CHARS(str)[0]);      return jsdouble(str->chars()[0]);
1033  }  }
1034    
1035    /*
1036     * The FuncFilter replaces the generic double version of charCodeAt with the
1037     * integer fast path if appropriate.
1038     */
1039  int32 FASTCALL  int32 FASTCALL
1040  js_String_p_charCodeAt0_int(JSString* str)  js_String_p_charCodeAt0_int(JSString* str)
1041  {  {
1042      if ((int32)JSSTRING_LENGTH(str) == 0)      if ((int32)str->length() == 0)
1043          return 0;          return 0;
1044      return JSSTRING_CHARS(str)[0];      return str->chars()[0];
1045  }  }
   
 /*  
  * The FuncFilter replaces the generic double version of charCodeAt with the  
  * integer fast path if appropriate.  
  */  
1046  JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING,        1, 1)  JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING,        1, 1)
 JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int,  STRING, INT32, 1, 1)  
1047  #endif  #endif
1048    
1049  jsint  jsint
1050  js_BoyerMooreHorspool(const jschar *text, jsint textlen,  js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1051                        const jschar *pat, jsint patlen,                        const jschar *pat, jsuint patlen)
                       jsint start)  
1052  {  {
1053      jsint i, j, k, m;      uint8 skip[sBMHCharSetSize];
     uint8 skip[BMH_CHARSET_SIZE];  
     jschar c;  
1054    
1055      JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX);      JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1056      for (i = 0; i < BMH_CHARSET_SIZE; i++)      for (jsuint i = 0; i < sBMHCharSetSize; i++)
1057          skip[i] = (uint8)patlen;          skip[i] = (uint8)patlen;
1058      m = patlen - 1;      jsuint m = patlen - 1;
1059      for (i = 0; i < m; i++) {      for (jsuint i = 0; i < m; i++) {
1060          c = pat[i];          jschar c = pat[i];
1061          if (c >= BMH_CHARSET_SIZE)          if (c >= sBMHCharSetSize)
1062              return BMH_BAD_PATTERN;              return sBMHBadPattern;
1063          skip[c] = (uint8)(m - i);          skip[c] = (uint8)(m - i);
1064      }      }
1065      for (k = start + m;      jschar c;
1066        for (jsuint k = m;
1067           k < textlen;           k < textlen;
1068           k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) {           k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1069          for (i = k, j = m; ; i--, j--) {          for (jsuint i = k, j = m; ; i--, j--) {
             if (j < 0)  
                 return i + 1;  
1070              if (text[i] != pat[j])              if (text[i] != pat[j])
1071                  break;                  break;
1072                if (j == 0)
1073                    return static_cast<jsint>(i);  /* safe: max string size */
1074          }          }
1075      }      }
1076      return -1;      return -1;
1077  }  }
1078    
1079  static JSBool  static JS_ALWAYS_INLINE jsint
1080  str_indexOf(JSContext *cx, uintN argc, jsval *vp)  StringMatch(const jschar *text, jsuint textlen,
1081                const jschar *pat, jsuint patlen)
1082  {  {
1083      jsval t;      if (patlen == 0)
1084      JSString *str, *str2;          return 0;
1085      const jschar *text, *pat;      if (textlen < patlen)
1086      jsint i, j, index, textlen, patlen;          return -1;
     jsdouble d;  
1087    
1088      t = vp[1];  #if __i386__
1089      if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_STRING(vp[2])) {      /*
1090          str = JSVAL_TO_STRING(t);       * Given enough registers, the unrolled loop below is faster than the
1091          str2 = JSVAL_TO_STRING(vp[2]);       * following loop. 32-bit x86 does not have enough registers.
1092      } else {       */
1093          str = NormalizeThis(cx, vp);      if (patlen == 1) {
1094          if (!str)          const jschar p0 = *pat;
1095              return JS_FALSE;          for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
1096                if (*c == p0)
1097                    return c - text;
1098            }
1099            return -1;
1100        }
1101    #endif
1102    
1103          str2 = ArgToRootedString(cx, argc, vp, 0);      /*
1104          if (!str2)       * XXX tune the BMH threshold (512) and pattern-length threshold (2).
1105              return JS_FALSE;       * If the text or pattern strings are short, BMH will be more expensive
1106         * than the basic linear scan.
1107         */
1108        if (textlen >= 512 && jsuint(patlen - 2) <= sBMHPatLenMax - 2) {
1109            jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
1110            if (index != sBMHBadPattern)
1111                return index;
1112      }      }
1113    
1114      text = JSSTRING_CHARS(str);      const jschar *textend = text + textlen - (patlen - 1);
1115      textlen = (jsint) JSSTRING_LENGTH(str);      const jschar *patend = pat + patlen;
1116      pat = JSSTRING_CHARS(str2);      const jschar p0 = *pat;
1117      patlen = (jsint) JSSTRING_LENGTH(str2);      const jschar *patNext = pat + 1;
1118        uint8 fixup;
1119    
1120      if (argc > 1) {  #if __APPLE__ && __GNUC__ && __i386__
1121          d = js_ValueToNumber(cx, &vp[3]);      /*
1122          if (JSVAL_IS_NULL(vp[3]))       * It is critical that |t| is kept in a register. The version of gcc we use
1123              return JS_FALSE;       * to build on 32-bit Mac does not realize this. See bug 526173.
1124          d = js_DoubleToInteger(d);       */
1125          if (d < 0)      register const jschar *t asm("esi") = text;
1126              i = 0;  #else
1127          else if (d > textlen)      const jschar *t = text;
1128              i = textlen;  #endif
         else  
             i = (jsint)d;  
     } else {  
         i = 0;  
     }  
     if (patlen == 0) {  
         *vp = INT_TO_JSVAL(i);  
         return JS_TRUE;  
     }  
1129    
1130      /* XXX tune the BMH threshold (512) */      /* Credit: Duff */
1131      if (textlen - i >= 512 && (jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2) {      switch ((textend - text) & 7) {
1132          index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i);          do {
1133          if (index != BMH_BAD_PATTERN)            case 0: if (*t++ == p0) { fixup = 8; goto match; }
1134              goto out;            case 7: if (*t++ == p0) { fixup = 7; goto match; }
1135              case 6: if (*t++ == p0) { fixup = 6; goto match; }
1136              case 5: if (*t++ == p0) { fixup = 5; goto match; }
1137              case 4: if (*t++ == p0) { fixup = 4; goto match; }
1138              case 3: if (*t++ == p0) { fixup = 3; goto match; }
1139              case 2: if (*t++ == p0) { fixup = 2; goto match; }
1140              case 1: if (*t++ == p0) { fixup = 1; goto match; }
1141                continue;
1142                do {
1143                    if (*t++ == p0) {
1144                      match:
1145                        for (const jschar *p1 = patNext, *t1 = t;
1146                             p1 != patend;
1147                             ++p1, ++t1) {
1148                            if (*p1 != *t1)
1149                                goto failed_match;
1150                        }
1151                        return t - text - 1;
1152                    }
1153                  failed_match:;
1154                } while (--fixup > 0);
1155            } while(t != textend);
1156      }      }
1157        return -1;
1158    }
1159    
1160      index = -1;  static JSBool
1161      j = 0;  str_indexOf(JSContext *cx, uintN argc, jsval *vp)
1162      while (i + j < textlen) {  {
1163          if (text[i + j] == pat[j]) {  
1164              if (++j == patlen) {      JSString *str;
1165                  index = i;      NORMALIZE_THIS(cx, vp, str);
1166                  break;  
1167        JSString *patstr = ArgToRootedString(cx, argc, vp, 0);
1168        if (!patstr)
1169            return JS_FALSE;
1170    
1171        const jschar *text = str->chars();
1172        jsuint textlen = str->length();
1173        const jschar *pat = patstr->chars();
1174        jsuint patlen = patstr->length();
1175    
1176        jsuint start;
1177        if (argc > 1) {
1178            jsval indexVal = vp[3];
1179            if (JSVAL_IS_INT(indexVal)) {
1180                jsint i = JSVAL_TO_INT(indexVal);
1181                if (i <= 0) {
1182                    start = 0;
1183                } else if (jsuint(i) > textlen) {
1184                    start = 0;
1185                    textlen = 0;
1186                } else {
1187                    start = i;
1188                    text += start;
1189                    textlen -= start;
1190              }              }
1191          } else {          } else {
1192              i++;              jsdouble d = js_ValueToNumber(cx, &vp[3]);
1193              j = 0;              if (JSVAL_IS_NULL(vp[3]))
1194                    return JS_FALSE;
1195                d = js_DoubleToInteger(d);
1196                if (d <= 0) {
1197                    start = 0;
1198                } else if (d > textlen) {
1199                    start = 0;
1200                    textlen = 0;
1201                } else {
1202                    start = (jsint)d;
1203                    text += start;
1204                    textlen -= start;
1205                }
1206          }          }
1207        } else {
1208            start = 0;
1209      }      }
1210    
1211  out:      jsint match = StringMatch(text, textlen, pat, patlen);
1212      *vp = INT_TO_JSVAL(index);      *vp = INT_TO_JSVAL((match == -1) ? -1 : start + match);
1213      return JS_TRUE;      return true;
1214  }  }
1215    
1216  static JSBool  static JSBool
# Line 1151  Line 1222 
1222      jsdouble d;      jsdouble d;
1223    
1224      NORMALIZE_THIS(cx, vp, str);      NORMALIZE_THIS(cx, vp, str);
1225      text = JSSTRING_CHARS(str);      text = str->chars();
1226      textlen = (jsint) JSSTRING_LENGTH(str);      textlen = (jsint) str->length();
1227    
1228      if (argc != 0 && JSVAL_IS_STRING(vp[2])) {      if (argc != 0 && JSVAL_IS_STRING(vp[2])) {
1229          str2 = JSVAL_TO_STRING(vp[2]);          str2 = JSVAL_TO_STRING(vp[2]);
# Line 1161  Line 1232 
1232          if (!str2)          if (!str2)
1233              return JS_FALSE;              return JS_FALSE;
1234      }      }
1235      pat = JSSTRING_CHARS(str2);      pat = str2->chars();
1236      patlen = (jsint) JSSTRING_LENGTH(str2);      patlen = (jsint) str2->length();
1237    
1238      i = textlen - patlen; // Start searching here      i = textlen - patlen; // Start searching here
1239      if (i < 0) {      if (i < 0) {
# Line 1196  Line 1267 
1267          return JS_TRUE;          return JS_TRUE;
1268      }      }
1269    
1270      j = 0;      const jschar *t = text + i;
1271      while (i >= 0) {      const jschar *textend = text - 1;
1272          /* This is always safe because i <= textlen - patlen and j < patlen */      const jschar p0 = *pat;
1273          if (text[i + j] == pat[j]) {      const jschar *patNext = pat + 1;
1274              if (++j == patlen)      const jschar *patEnd = pat + patlen;
1275                  break;  
1276          } else {      for (; t != textend; --t) {
1277              i--;          if (*t == p0) {
1278              j = 0;              const jschar *t1 = t + 1;
1279                for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
1280                    if (*t1 != *p1)
1281                        goto break_continue;
1282                }
1283                *vp = INT_TO_JSVAL(t - text);
1284                return JS_TRUE;
1285          }          }
1286          break_continue:;
1287      }      }
1288      *vp = INT_TO_JSVAL(i);  
1289        *vp = INT_TO_JSVAL(-1);
1290      return JS_TRUE;      return JS_TRUE;
1291  }  }
1292    
# Line 1219  Line 1298 
1298      size_t length, begin, end;      size_t length, begin, end;
1299    
1300      NORMALIZE_THIS(cx, vp, str);      NORMALIZE_THIS(cx, vp, str);
1301      JSSTRING_CHARS_AND_LENGTH(str, chars, length);      str->getCharsAndLength(chars, length);
1302      begin = 0;      begin = 0;
1303      end = length;      end = length;
1304    
# Line 1262  Line 1341 
1341  /*  /*
1342   * Perl-inspired string functions.   * Perl-inspired string functions.
1343   */   */
 typedef struct GlobData {  
     jsbytecode  *pc;            /* in: program counter resulting in us matching */  
     uintN       flags;          /* inout: mode and flag bits, see below */  
     uintN       optarg;         /* in: index of optional flags argument */  
     JSString    *str;           /* out: 'this' parameter object as string */  
     JSRegExp    *regexp;        /* out: regexp parameter object private data */  
 } GlobData;  
1344    
1345  /*  /*
1346   * Mode and flag bit definitions for match_or_replace's GlobData.flags field.   * RegExpGuard factors logic out of String regexp operations. After each
1347     * operation completes, RegExpGuard data members become available, according to
1348     * the comments below.
1349     *
1350     * Notes on parameters to RegExpGuard member functions:
1351     *  - 'optarg' indicates in which argument position RegExp flags will be found,
1352     *    if present. This is a Mozilla extension and not part of any ECMA spec.
1353     *  - 'flat' indicates that the given pattern string will not be interpreted as
1354     *    a regular expression, hence regexp meta-characters are ignored.
1355   */   */
1356  #define MODE_MATCH      0x00    /* in: return match array on success */  class RegExpGuard
 #define MODE_REPLACE    0x01    /* in: match and replace */  
 #define MODE_SEARCH     0x02    /* in: search only, return match index or -1 */  
 #define GET_MODE(f)     ((f) & 0x03)  
 #define FORCE_FLAT      0x04    /* in: force flat (non-regexp) string match */  
 #define KEEP_REGEXP     0x08    /* inout: keep GlobData.regexp alive for caller  
                                           of match_or_replace; if set on input  
                                           but clear on output, regexp ownership  
                                           does not pass to caller */  
 #define GLOBAL_REGEXP   0x10    /* out: regexp had the 'g' flag */  
   
 static JSBool  
 match_or_replace(JSContext *cx,  
                  JSBool (*glob)(JSContext *cx, jsint count, GlobData *data),  
                  void (*destroy)(JSContext *cx, GlobData *data),  
                  GlobData *data, uintN argc, jsval *vp)  
1357  {  {
1358      JSString *str, *src, *opt;      RegExpGuard(const RegExpGuard &);
1359      JSObject *reobj;      void operator=(const RegExpGuard &);
     JSRegExp *re;  
     size_t index, length;  
     JSBool ok, test;  
     jsint count;  
1360    
1361      NORMALIZE_THIS(cx, vp, str);      JSContext *mCx;
1362      data->str = str;      JSObject *mReobj;
1363        JSRegExp *mRe;
1364    
1365      public:
1366        RegExpGuard(JSContext *cx) : mCx(cx), mRe(NULL) {}
1367    
1368        ~RegExpGuard() {
1369            if (mRe)
1370                DROP_REGEXP(mCx, mRe);
1371        }
1372    
1373        /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
1374        bool
1375        init(uintN argc, jsval *vp)
1376        {
1377            jsval patval = vp[2];
1378            if (argc != 0 && VALUE_IS_REGEXP(mCx, patval)) {
1379                mReobj = JSVAL_TO_OBJECT(patval);
1380                mRe = (JSRegExp *) mReobj->getPrivate();
1381                HOLD_REGEXP(mCx, mRe);
1382            } else {
1383                patstr = ArgToRootedString(mCx, argc, vp, 0);
1384                if (!patstr)
1385                    return false;
1386            }
1387            return true;
1388        }
1389    
1390      if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {      /*
1391          reobj = JSVAL_TO_OBJECT(vp[2]);       * Upper bound on the number of characters we are willing to potentially
1392          re = (JSRegExp *) JS_GetPrivate(cx, reobj);       * waste on searching for RegExp meta-characters.
1393      } else {       */
1394          src = ArgToRootedString(cx, argc, vp, 0);      static const size_t sMaxFlatPatLen = 256;
1395          if (!src)  
1396              return JS_FALSE;      /*
1397          if (data->optarg < argc) {       * Attempt to match |patstr| with |textstr|.  Return false if flat matching
1398              opt = js_ValueToString(cx, vp[2 + data->optarg]);       * could not be used.
1399         */
1400        bool
1401        tryFlatMatch(JSString *textstr, bool flat, uintN optarg, uintN argc)
1402        {
1403            if (mRe)
1404                return false;
1405            patstr->getCharsAndLength(pat, patlen);
1406            if (optarg < argc ||
1407                (!flat &&
1408                 (patlen > sMaxFlatPatLen || js_ContainsRegExpMetaChars(pat, patlen)))) {
1409                return false;
1410            }
1411            textstr->getCharsAndLength(text, textlen);
1412            match = StringMatch(text, textlen, pat, patlen);
1413            return true;
1414        }
1415    
1416        /* Data available on successful return from |tryFlatMatch|. */
1417        JSString *patstr;
1418        const jschar *pat;
1419        size_t patlen;
1420        const jschar *text;
1421        size_t textlen;
1422        jsint match;
1423    
1424        /* If the pattern is not already a regular expression, make it so. */
1425        bool
1426        normalizeRegExp(bool flat, uintN optarg, uintN argc, jsval *vp)
1427        {
1428            /* If we don't have a RegExp, build RegExp from pattern string. */
1429            if (mRe)
1430                return true;
1431            JSString *opt;
1432            if (optarg < argc) {
1433                opt = js_ValueToString(mCx, vp[2 + optarg]);
1434              if (!opt)              if (!opt)
1435                  return JS_FALSE;                  return false;
1436          } else {          } else {
1437              opt = NULL;              opt = NULL;
1438          }          }
1439          re = js_NewRegExpOpt(cx, src, opt, (data->flags & FORCE_FLAT) != 0);          mRe = js_NewRegExpOpt(mCx, patstr, opt, flat);
1440          if (!re)          if (!mRe)
1441              return JS_FALSE;              return false;
1442          reobj = NULL;          mReobj = NULL;
1443            return true;
1444      }      }
1445      /* From here on, all control flow must reach the matching DROP. */  
1446      data->regexp = re;      /* Data available on successful return from |normalizeRegExp|. */
1447      HOLD_REGEXP(cx, re);      JSObject *reobj() const { return mReobj; }  /* nullable */
1448        JSRegExp *re() const { return mRe; }        /* non-null */
1449      if (re->flags & JSREG_GLOB)  };
1450          data->flags |= GLOBAL_REGEXP;  
1451      index = 0;  /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
1452      if (GET_MODE(data->flags) == MODE_SEARCH) {  static JS_ALWAYS_INLINE bool
1453          ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, vp);  Matched(bool test, jsval v)
1454          if (ok) {  {
1455              *vp = (*vp == JSVAL_TRUE)      return test ? (v == JSVAL_TRUE) : !JSVAL_IS_NULL(v);
1456                    ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length)  }
1457                    : INT_TO_JSVAL(-1);  
1458          }  typedef bool (*DoMatchCallback)(JSContext *cx, size_t count, void *data);
1459      } else if (data->flags & GLOBAL_REGEXP) {  
1460          if (reobj) {  /*
1461              /* Set the lastIndex property's reserved slot to 0. */   * BitOR-ing these flags allows the DoMatch caller to control when how the
1462              ok = js_SetLastIndex(cx, reobj, 0);   * RegExp engine is called and when callbacks are fired.
1463          } else {   */
1464              ok = JS_TRUE;  enum MatchControlFlags {
1465          }     TEST_GLOBAL_BIT         = 0x1, /* use RegExp.test for global regexps */
1466          if (ok) {     TEST_SINGLE_BIT         = 0x2, /* use RegExp.test for non-global regexps */
1467              length = JSSTRING_LENGTH(str);     CALLBACK_ON_SINGLE_BIT  = 0x4, /* fire callback on non-global match */
1468              for (count = 0; index <= length; count++) {  
1469                  ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, vp);     MATCH_ARGS    = TEST_GLOBAL_BIT,
1470                  if (!ok || *vp != JSVAL_TRUE)     MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
1471                      break;     REPLACE_ARGS  = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
1472                  ok = glob(cx, count, data);  };
1473                  if (!ok)  
1474                      break;  /* Factor out looping and matching logic. */
1475                  if (cx->regExpStatics.lastMatch.length == 0) {  static bool
1476                      if (index == length)  DoMatch(JSContext *cx, jsval *vp, JSString *str, const RegExpGuard &g,
1477                          break;          DoMatchCallback callback, void *data, MatchControlFlags flags)
1478                      index++;  {
1479                  }      if (g.re()->flags & JSREG_GLOB) {
1480              }          /* global matching ('g') */
1481              if (!ok && destroy)          bool testGlobal = flags & TEST_GLOBAL_BIT;
1482                  destroy(cx, data);          if (g.reobj())
1483                js_ClearRegExpLastIndex(g.reobj());
1484            for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
1485                if (!js_ExecuteRegExp(cx, g.re(), str, &i, testGlobal, vp))
1486                    return false;
1487                if (!Matched(testGlobal, *vp))
1488                    break;
1489                if (!callback(cx, count, data))
1490                    return false;
1491                if (cx->regExpStatics.lastMatch.length == 0)
1492                    ++i;
1493          }          }
1494      } else {      } else {
1495          if (GET_MODE(data->flags) == MODE_REPLACE) {          /* single match */
1496              test = JS_TRUE;          bool testSingle = flags & TEST_SINGLE_BIT,
1497          } else {               callbackOnSingle = flags & CALLBACK_ON_SINGLE_BIT;
1498              /*          size_t i = 0;
1499               * MODE_MATCH implies str_match is being called from a script or a          if (!js_ExecuteRegExp(cx, g.re(), str, &i, testSingle, vp))
1500               * scripted function.  If the caller cares only about testing null              return false;
1501               * vs. non-null return value, optimize away the array object that          if (callbackOnSingle && Matched(testSingle, *vp) &&
1502               * would normally be returned in *vp.              !callback(cx, 0, data)) {
1503               *              return false;
              * Assume a full array result is required, then prove otherwise.  
              */  
             test = JS_FALSE;  
             if (data->pc && (*data->pc == JSOP_CALL || *data->pc == JSOP_NEW)) {  
                 JS_ASSERT(js_CodeSpec[*data->pc].length == 3);  
                 switch (data->pc[3]) {  
                   case JSOP_POP:  
                   case JSOP_IFEQ:  
                   case JSOP_IFNE:  
                   case JSOP_IFEQX:  
                   case JSOP_IFNEX:  
                     test = JS_TRUE;  
                     break;  
                   default:;  
                 }  
             }  
1504          }          }
         ok = js_ExecuteRegExp(cx, re, str, &index, test, vp);  
1505      }      }
1506        return true;
     DROP_REGEXP(cx, re);  
     if (reobj) {  
         /* Tell our caller that it doesn't need to destroy data->regexp. */  
         data->flags &= ~KEEP_REGEXP;  
     } else if (!ok || !(data->flags & KEEP_REGEXP)) {  
         /* Caller didn't want to keep data->regexp, so null and destroy it.  */  
         data->regexp = NULL;  
         js_DestroyRegExp(cx, re);  
     }  
   
     return ok;  
1507  }  }
1508    
1509  typedef struct MatchData {  /*
1510      GlobData    base;   * DoMatch will only callback on global matches, hence this function builds
1511      jsval       *arrayval;      /* NB: local root pointer */   * only the "array of matches" returned by match on global regexps.
1512  } MatchData;   */
1513    static bool
1514  static JSBool  MatchCallback(JSContext *cx, size_t count, void *p)
 match_glob(JSContext *cx, jsint count, GlobData *data)  
1515  {  {
1516      MatchData *mdata;      JS_ASSERT(count <= JSVAL_INT_MAX);  /* by max string length */
     JSObject *arrayobj;  
     JSSubString *matchsub;  
     JSString *matchstr;  
     jsval v;  
1517    
1518      mdata = (MatchData *)data;      jsval &arrayval = *static_cast<jsval *>(p);
1519      arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval);      JSObject *arrayobj = JSVAL_TO_OBJECT(arrayval);
1520      if (!arrayobj) {      if (!arrayobj) {
1521          arrayobj = js_NewArrayObject(cx, 0, NULL);          arrayobj = js_NewArrayObject(cx, 0, NULL);
1522          if (!arrayobj)          if (!arrayobj)
1523              return JS_FALSE;              return false;
1524          *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj);          arrayval = OBJECT_TO_JSVAL(arrayobj);
1525      }      }
1526      matchsub = &cx->regExpStatics.lastMatch;  
1527      matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length);      JSString *str = cx->regExpStatics.input;
1528        JSSubString &match = cx->regExpStatics.lastMatch;
1529        ptrdiff_t off = match.chars - str->chars();
1530        JS_ASSERT(off >= 0 && size_t(off) <= str->length());
1531        JSString *matchstr = js_NewDependentString(cx, str, off, match.length);
1532      if (!matchstr)      if (!matchstr)
1533          return JS_FALSE;          return false;
1534      v = STRING_TO_JSVAL(matchstr);  
1535      JS_ASSERT(count <= JSVAL_INT_MAX);      jsval v = STRING_TO_JSVAL(matchstr);
1536    
1537      JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);      JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1538      return OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(count), &v);      return arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
1539  }  }
1540    
1541  static JSBool  static bool
1542  StringMatchHelper(JSContext *cx, uintN argc, jsval *vp, jsbytecode *pc)  BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g,
1543                        jsval *vp)
1544  {  {
1545      JSTempValueRooter tvr;      if (g.match < 0) {
1546      MatchData mdata;          *vp = JSVAL_NULL;
1547      JSBool ok;          return true;
1548        }
1549    
1550      JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);      /* For this non-global match, produce a RegExp.exec-style array. */
1551      mdata.base.pc = pc;      JSObject *obj = js_NewSlowArrayObject(cx);
1552      mdata.base.flags = MODE_MATCH;      if (!obj)
1553      mdata.base.optarg = 1;          return false;
1554      mdata.arrayval = &tvr.u.value;      *vp = OBJECT_TO_JSVAL(obj);
1555      ok = match_or_replace(cx, match_glob, NULL, &mdata.base, argc, vp);  
1556      if (ok && !JSVAL_IS_NULL(*mdata.arrayval))      return obj->defineProperty(cx, INT_TO_JSID(0), STRING_TO_JSVAL(g.patstr)) &&
1557          *vp = *mdata.arrayval;             obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
1558      JS_POP_TEMP_ROOT(cx, &tvr);                                 INT_TO_JSVAL(g.match)) &&
1559      return ok;             obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
1560                                   STRING_TO_JSVAL(textstr));
1561  }  }
1562    
1563  static JSBool  static JSBool
1564  str_match(JSContext *cx, uintN argc, jsval *vp)  str_match(JSContext *cx, uintN argc, jsval *vp)
1565  {  {
1566      return StringMatchHelper(cx, argc, vp, js_GetCurrentBytecodePC(cx));      JSString *str;
1567        NORMALIZE_THIS(cx, vp, str);
1568    
1569        RegExpGuard g(cx);
1570        if (!g.init(argc, vp))
1571            return false;
1572        if (g.tryFlatMatch(str, false, 1, argc))
1573            return BuildFlatMatchArray(cx, str, g, vp);
1574        if (!g.normalizeRegExp(false, 1, argc, vp))
1575            return false;
1576    
1577        JSAutoTempValueRooter array(cx, JSVAL_NULL);
1578        if (!DoMatch(cx, vp, str, g, MatchCallback, array.addr(), MATCH_ARGS))
1579            return false;
1580    
1581        /* When not global, DoMatch will leave |RegEx.exec()| in *vp. */
1582        if (g.re()->flags & JSREG_GLOB)
1583            *vp = array.value();
1584        return true;
1585  }  }
1586    
1587  static JSBool  static JSBool
1588  str_search(JSContext *cx, uintN argc, jsval *vp)  str_search(JSContext *cx, uintN argc, jsval *vp)
1589  {  {
1590      GlobData data;      JSString *str;
1591        NORMALIZE_THIS(cx, vp, str);
1592    
1593      data.flags = MODE_SEARCH;      RegExpGuard g(cx);
1594      data.optarg = 1;      if (!g.init(argc, vp))
1595      return match_or_replace(cx, NULL, NULL, &data, argc, vp);          return false;
1596  }      if (g.tryFlatMatch(str, false, 1, argc)) {
1597            *vp = INT_TO_JSVAL(g.match);
1598  typedef struct ReplaceData {          return true;
1599      GlobData    base;           /* base struct state */      }
1600      JSObject    *lambda;        /* replacement function object or null */      if (!g.normalizeRegExp(false, 1, argc, vp))
1601      JSString    *repstr;        /* replacement string */          return false;
1602      jschar      *dollar;        /* null or pointer to first $ in repstr */  
1603      jschar      *dollarEnd;     /* limit pointer for js_strchr_limit */      size_t i = 0;
1604      jschar      *chars;         /* result chars, null initially */      if (!js_ExecuteRegExp(cx, g.re(), str, &i, true, vp))
1605      size_t      length;         /* result length, 0 initially */          return false;
1606      jsint       index;          /* index in result of next replacement */  
1607      jsint       leftIndex;      /* left context index in base.str->chars */      if (*vp == JSVAL_TRUE)
1608      JSSubString dollarStr;      /* for "$$" interpret_dollar result */          *vp = INT_TO_JSVAL(cx->regExpStatics.leftContext.length);
1609  } ReplaceData;      else
1610            *vp = INT_TO_JSVAL(-1);
1611        return true;
1612    }
1613    
1614    struct ReplaceData {
1615        ReplaceData(JSContext *cx) : g(cx), cb(cx) {}
1616        JSString      *str;           /* 'this' parameter object as a string */
1617        RegExpGuard   g;              /* regexp parameter object and private data */
1618        JSObject      *lambda;        /* replacement function object or null */
1619        JSString      *repstr;        /* replacement string */
1620        jschar        *dollar;        /* null or pointer to first $ in repstr */
1621        jschar        *dollarEnd;     /* limit pointer for js_strchr_limit */
1622        jsint         index;          /* index in result of next replacement */
1623        jsint         leftIndex;      /* left context index in str->chars */
1624        JSSubString   dollarStr;      /* for "$$" InterpretDollar result */
1625        bool          calledBack;     /* record whether callback has been called */
1626        JSCharBuffer  cb;             /* buffer built during DoMatch */
1627    };
1628    
1629  static JSSubString *  static JSSubString *
1630  interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata,  InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata,
1631                   size_t *skip)                  size_t *skip)
1632  {  {
1633      JSRegExpStatics *res;      JSRegExpStatics *res;
1634      jschar dc, *cp;      jschar dc, *cp;
# Line 1525  Line 1669 
1669      *skip = 2;      *skip = 2;
1670      switch (dc) {      switch (dc) {
1671        case '$':        case '$':
1672          rdata->dollarStr.chars = dp;          rdata.dollarStr.chars = dp;
1673          rdata->dollarStr.length = 1;          rdata.dollarStr.length = 1;
1674          return &rdata->dollarStr;          return &rdata.dollarStr;
1675        case '&':        case '&':
1676          return &res->lastMatch;          return &res->lastMatch;
1677        case '+':        case '+':
# Line 1540  Line 1684 
1684      return NULL;      return NULL;
1685  }  }
1686    
1687  static JS_REQUIRES_STACK JSBool  static JS_ALWAYS_INLINE bool
1688  find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)  PushRegExpSubstr(JSContext *cx, const JSSubString &sub, jsval *&sp)
1689    {
1690        JSString *whole = cx->regExpStatics.input;
1691        size_t off = sub.chars - whole->chars();
1692        JSString *str = js_NewDependentString(cx, whole, off, sub.length);
1693        if (!str)
1694            return false;
1695        *sp++ = STRING_TO_JSVAL(str);
1696        return true;
1697    }
1698    
1699    static bool
1700    FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
1701  {  {
1702      JSString *repstr;      JSString *repstr;
1703      size_t replen, skip;      size_t replen, skip;
# Line 1549  Line 1705 
1705      JSSubString *sub;      JSSubString *sub;
1706      JSObject *lambda;      JSObject *lambda;
1707    
1708      lambda = rdata->lambda;      lambda = rdata.lambda;
1709      if (lambda) {      if (lambda) {
1710          uintN argc, i, j, m, n, p;          uintN i, m, n;
         jsval *invokevp, *sp;  
         void *mark;  
         JSBool ok;  
1711    
1712          /*          js_LeaveTrace(cx);
          * Save the regExpStatics from the current regexp, since they may be  
          * clobbered by a RegExp usage in the lambda function.  Note that all  
          * members of JSRegExpStatics are JSSubStrings, so not GC roots, save  
          * input, which is rooted otherwise via vp[1] in str_replace.  
          */  
         JSRegExpStatics save = cx->regExpStatics;  
         JSBool freeMoreParens = JS_FALSE;  
1713    
1714          /*          /*
1715           * In the lambda case, not only do we find the replacement string's           * In the lambda case, not only do we find the replacement string's
1716           * length, we compute repstr and return it via rdata for use within           * length, we compute repstr and return it via rdata for use within
1717           * do_replace.  The lambda is called with arguments ($&, $1, $2, ...,           * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
1718           * index, input), i.e., all the properties of a regexp match array.           * index, input), i.e., all the properties of a regexp match array.
1719           * For $&, etc., we must create string jsvals from cx->regExpStatics.           * For $&, etc., we must create string jsvals from cx->regExpStatics.
1720           * We grab up stack space to keep the newborn strings GC-rooted.           * We grab up stack space to keep the newborn strings GC-rooted.
1721           */           */
1722          p = rdata->base.regexp->parenCount;          uintN p = rdata.g.re()->parenCount;
1723          argc = 1 + p + 2;          uintN argc = 1 + p + 2;
1724          invokevp = js_AllocStack(cx, 2 + argc, &mark);          void *mark;
1725            jsval *invokevp = js_AllocStack(cx, 2 + argc, &mark);
1726          if (!invokevp)          if (!invokevp)
1727              return JS_FALSE;              return false;
1728    
1729            MUST_FLOW_THROUGH("lambda_out");
1730            bool ok = false;
1731            bool freeMoreParens = false;
1732    
1733            /*
1734             * Save the regExpStatics from the current regexp, since they may be
1735             * clobbered by a RegExp usage in the lambda function.  Note that all
1736             * members of JSRegExpStatics are JSSubStrings, so not GC roots, save
1737             * input, which is rooted otherwise via vp[1] in str_replace.
1738             */
1739            JSRegExpStatics save = cx->regExpStatics;
1740    
1741          /* Push lambda and its 'this' parameter. */          /* Push lambda and its 'this' parameter. */
1742          sp = invokevp;          jsval *sp = invokevp;
1743          *sp++ = OBJECT_TO_JSVAL(lambda);          *sp++ = OBJECT_TO_JSVAL(lambda);
1744          *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));          *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));
1745    
 #define PUSH_REGEXP_STATIC(sub)                                               \  
     JS_BEGIN_MACRO                                                            \  
         JSString *str = js_NewStringCopyN(cx,                                 \  
                                           cx->regExpStatics.sub.chars,        \  
                                           cx->regExpStatics.sub.length);      \  
         if (!str) {                                                           \  
             ok = JS_FALSE;                                                    \  
             goto lambda_out;                                                  \  
         }                                                                     \  
         *sp++ = STRING_TO_JSVAL(str);                                         \  
     JS_END_MACRO  
   
1746          /* Push $&, $1, $2, ... */          /* Push $&, $1, $2, ... */
1747          PUSH_REGEXP_STATIC(lastMatch);          if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
1748                goto lambda_out;
1749    
1750          i = 0;          i = 0;
1751          m = cx->regExpStatics.parenCount;          m = cx->regExpStatics.parenCount;
1752          n = JS_MIN(m, 9);          n = JS_MIN(m, 9);
1753          for (j = 0; i < n; i++, j++)          for (uintN j = 0; i < n; i++, j++) {
1754              PUSH_REGEXP_STATIC(parens[j]);              if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
1755          for (j = 0; i < m; i++, j++)                  goto lambda_out;
1756              PUSH_REGEXP_STATIC(moreParens[j]);          }
1757            for (uintN j = 0; i < m; i++, j++) {
1758                if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp))
1759                    goto lambda_out;
1760            }
1761    
1762          /*          /*
1763           * We need to clear moreParens in the top-of-stack cx->regExpStatics           * We need to clear moreParens in the top-of-stack cx->regExpStatics
1764           * to it won't be possibly realloc'ed, leaving the bottom-of-stack           * so it won't be possibly realloc'ed, leaving the bottom-of-stack
1765           * moreParens pointing to freed memory.           * moreParens pointing to freed memory.
1766           */           */
1767          cx->regExpStatics.moreParens = NULL;          cx->regExpStatics.moreParens = NULL;
1768          freeMoreParens = JS_TRUE;          freeMoreParens = true;
   
 #undef PUSH_REGEXP_STATIC  
1769    
1770          /* Make sure to push undefined for any unmatched parens. */          /* Make sure to push undefined for any unmatched parens. */
1771          for (; i < p; i++)          for (; i < p; i++)
# Line 1622  Line 1773 
1773    
1774          /* Push match index and input string. */          /* Push match index and input string. */
1775          *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);          *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
1776          *sp++ = STRING_TO_JSVAL(rdata->base.str);          *sp++ = STRING_TO_JSVAL(rdata.str);
1777    
1778          ok = js_Invoke(cx, argc, invokevp, 0);          if (!js_Invoke(cx, argc, invokevp, 0))
1779          if (ok) {              goto lambda_out;
1780              /*  
1781               * NB: we count on the newborn string root to hold any string          /*
1782               * created by this js_ValueToString that would otherwise be GC-           * NB: we count on the newborn string root to hold any string
1783               * able, until we use rdata->repstr in do_replace.           * created by this js_ValueToString that would otherwise be GC-
1784               */           * able, until we use rdata.repstr in DoReplace.
1785              repstr = js_ValueToString(cx, *invokevp);           */
1786              if (!repstr) {          repstr = js_ValueToString(cx, *invokevp);
1787                  ok = JS_FALSE;          if (!repstr)
1788              } else {              goto lambda_out;
1789                  rdata->repstr = repstr;  
1790                  *sizep = JSSTRING_LENGTH(repstr);          rdata.repstr = repstr;
1791              }          *sizep = repstr->length();
1792          }  
1793            ok = true;
1794    
1795        lambda_out:        lambda_out:
1796          js_FreeStack(cx, mark);          js_FreeStack(cx, mark);
1797          if (freeMoreParens)          if (freeMoreParens)
1798              JS_free(cx, cx->regExpStatics.moreParens);              cx->free(cx->regExpStatics.moreParens);
1799          cx->regExpStatics = save;          cx->regExpStatics = save;
1800          return ok;          return ok;
1801      }      }
1802    
1803      repstr = rdata->repstr;      repstr = rdata.repstr;
1804      replen = JSSTRING_LENGTH(repstr);      replen = repstr->length();
1805      for (dp = rdata->dollar, ep = rdata->dollarEnd; dp;      for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
1806           dp = js_strchr_limit(dp, '$', ep)) {           dp = js_strchr_limit(dp, '$', ep)) {
1807          sub = interpret_dollar(cx, dp, ep, rdata, &skip);          sub = InterpretDollar(cx, dp, ep, rdata, &skip);
1808          if (sub) {          if (sub) {
1809              replen += sub->length - skip;              replen += sub->length - skip;
1810              dp += skip;              dp += skip;
# Line 1661  Line 1813 
1813              dp++;              dp++;
1814      }      }
1815      *sizep = replen;      *sizep = replen;
1816      return JS_TRUE;      return true;
1817  }  }
1818    
1819  static void  static void
1820  do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars)  DoReplace(JSContext *cx, ReplaceData &rdata, jschar *chars)
1821  {  {
1822      JSString *repstr;      JSString *repstr;
1823      jschar *bp, *cp, *dp, *ep;      jschar *bp, *cp, *dp, *ep;
1824      size_t len, skip;      size_t len, skip;
1825      JSSubString *sub;      JSSubString *sub;
1826    
1827      repstr = rdata->repstr;      repstr = rdata.repstr;
1828      bp = cp = JSSTRING_CHARS(repstr);      bp = cp = repstr->chars();
1829      for (dp = rdata->dollar, ep = rdata->dollarEnd; dp;      for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
1830           dp = js_strchr_limit(dp, '$', ep)) {           dp = js_strchr_limit(dp, '$', ep)) {
1831          len = dp - cp;          len = dp - cp;
1832          js_strncpy(chars, cp, len);          js_strncpy(chars, cp, len);
1833          chars += len;          chars += len;
1834          cp = dp;          cp = dp;
1835          sub = interpret_dollar(cx, dp, ep, rdata, &skip);          sub = InterpretDollar(cx, dp, ep, rdata, &skip);
1836          if (sub) {          if (sub) {
1837              len = sub->length;              len = sub->length;
1838              js_strncpy(chars, sub->chars, len);              js_strncpy(chars, sub->chars, len);
# Line 1691  Line 1843 
1843              dp++;              dp++;
1844          }          }
1845      }      }
1846      js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp));      js_strncpy(chars, cp, repstr->length() - (cp - bp));
1847  }  }
1848    
1849  static void  static bool
1850  replace_destroy(JSContext *cx, GlobData *data)  ReplaceCallback(JSContext *cx, size_t count, void *p)
1851  {  {
1852      ReplaceData *rdata;      ReplaceData &rdata = *static_cast<ReplaceData *>(p);
1853    
1854      rdata = (ReplaceData *)data;      rdata.calledBack = true;
1855      JS_free(cx, rdata->chars);      JSString *str = rdata.str;
1856      rdata->chars = NULL;      size_t leftoff = rdata.leftIndex;
1857  }      const jschar *left = str->chars() + leftoff;
1858        size_t leftlen = cx->regExpStatics.lastMatch.chars - left;
1859        rdata.leftIndex = cx->regExpStatics.lastMatch.chars - str->chars();
1860        rdata.leftIndex += cx->regExpStatics.lastMatch.length;
1861    
1862  static JS_REQUIRES_STACK JSBool      size_t replen = 0;  /* silence 'unused' warning */
1863  replace_glob(JSContext *cx, jsint count, GlobData *data)      if (!FindReplaceLength(cx, rdata, &replen))
1864  {          return false;
     ReplaceData *rdata;  
     JSString *str;  
     size_t leftoff, leftlen, replen, growth;  
     const jschar *left;  
     jschar *chars;  
1865    
1866      rdata = (ReplaceData *)data;      size_t growth = leftlen + replen;
1867      str = data->str;      if (!rdata.cb.growBy(growth))
1868      leftoff = rdata->leftIndex;          return false;
1869      left = JSSTRING_CHARS(str) + leftoff;  
1870      leftlen = cx->regExpStatics.lastMatch.chars - left;      jschar *chars = rdata.cb.begin() + rdata.index;
1871      rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str);      rdata.index += growth;
     rdata->leftIndex += cx->regExpStatics.lastMatch.length;  
     if (!find_replen(cx, rdata, &replen))  
         return JS_FALSE;  
     growth = leftlen + replen;  
     chars = (jschar *)  
         (rdata->chars  
          ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1)  
                                         * sizeof(jschar))  
          : JS_malloc(cx, (growth + 1) * sizeof(jschar)));  
     if (!chars)  
         return JS_FALSE;  
     rdata->chars = chars;  
     rdata->length += growth;  
     chars += rdata->index;  
     rdata->index += growth;  
1872      js_strncpy(chars, left, leftlen);      js_strncpy(chars, left, leftlen);
1873      chars += leftlen;      chars += leftlen;
1874      do_replace(cx, rdata, chars);      DoReplace(cx, rdata, chars);
1875      return JS_TRUE;      return true;
1876  }  }
1877    
1878  static JS_REQUIRES_STACK JSBool  static bool
1879  str_replace(JSContext *cx, uintN argc, jsval *vp)  BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
1880                         const RegExpGuard &g, jsval *vp)
1881  {  {
1882      JSObject *lambda;      if (g.match == -1) {
1883      JSString *repstr;          *vp = STRING_TO_JSVAL(textstr);
1884            return true;
1885        }
1886    
1887      if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {      const jschar *rep;
1888          lambda = JSVAL_TO_OBJECT(vp[3]);      size_t replen;
1889          repstr = NULL;      repstr->getCharsAndLength(rep, replen);
1890      } else {  
1891          lambda = NULL;      JSCharBuffer cb(cx);
1892          repstr = ArgToRootedString(cx, argc, vp, 1);      if (!cb.reserve(g.textlen - g.patlen + replen) ||
1893          if (!repstr)          !cb.append(g.text, static_cast<size_t>(g.match)) ||
1894              return JS_FALSE;          !cb.append(rep, replen) ||
1895            !cb.append(g.text + g.match + g.patlen, g.text + g.textlen)) {
1896            return false;
1897      }      }
1898    
1899      return js_StringReplaceHelper(cx, argc, lambda, repstr, vp);      JSString *str = js_NewStringFromCharBuffer(cx, cb);
1900        if (!str)
1901            return false;
1902        *vp = STRING_TO_JSVAL(str);
1903        return true;
1904  }  }
1905    
1906  JSBool JS_REQUIRES_STACK  static JSBool
1907  js_StringReplaceHelper(JSContext *cx, uintN argc, JSObject *lambda,  str_replace(JSContext *cx, uintN argc, jsval *vp)
                        JSString *repstr, jsval *vp)  
1908  {  {
1909      ReplaceData rdata;      ReplaceData rdata(cx);
1910      JSBool ok;      NORMALIZE_THIS(cx, vp, rdata.str);
     size_t leftlen, rightlen, length;  
     jschar *chars;  
     JSString *str;  
1911    
1912      /*      /* Extract replacement string/function. */
1913       * For ECMA Edition 3, the first argument is to be converted to a string      if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {
1914       * to match in a "flat" sense (without regular expression metachars having          rdata.lambda = JSVAL_TO_OBJECT(vp[3]);
1915       * special meanings) UNLESS the first arg is a RegExp object.          rdata.repstr = NULL;
      */  
     rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT;  
     rdata.base.optarg = 2;  
   
     rdata.lambda = lambda;  
     rdata.repstr = repstr;  
     if (repstr) {  
         if (!js_MakeStringImmutable(cx, repstr))  
             return JS_FALSE;  
         rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr);  
         rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$',  
                                        rdata.dollarEnd);  
     } else {  
1916          rdata.dollar = rdata.dollarEnd = NULL;          rdata.dollar = rdata.dollarEnd = NULL;
1917        } else {
1918            rdata.lambda = NULL;
1919            rdata.repstr = ArgToRootedString(cx, argc, vp, 1);
1920            if (!rdata.repstr)
1921                return false;
1922    
1923            /* We're about to store pointers into the middle of our string. */
1924            if (!js_MakeStringImmutable(cx, rdata.repstr))
1925                return false;
1926            rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length();
1927            rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$',
1928                                           rdata.dollarEnd);
1929      }      }
1930      rdata.chars = NULL;  
1931      rdata.length = 0;      if (!rdata.g.init(argc, vp))
1932            return false;
1933        if (!rdata.dollar && !rdata.lambda &&
1934            rdata.g.tryFlatMatch(rdata.str, true, 2, argc)) {
1935            return BuildFlatReplacement(cx, rdata.str, rdata.repstr, rdata.g, vp);
1936        }
1937        if (!rdata.g.normalizeRegExp(true, 2, argc, vp))
1938            return false;
1939    
1940      rdata.index = 0;      rdata.index = 0;
1941      rdata.leftIndex = 0;      rdata.leftIndex = 0;
1942        rdata.calledBack = false;
1943    
1944      ok = match_or_replace(cx, replace_glob, replace_destroy, &rdata.base,      if (!DoMatch(cx, vp, rdata.str, rdata.g, ReplaceCallback, &rdata, REPLACE_ARGS))
1945                            argc, vp);          return false;
     if (!ok)  
         return JS_FALSE;  
1946    
1947      if (!rdata.chars) {      if (!rdata.calledBack) {
1948          if ((rdata.base.flags & GLOBAL_REGEXP) || *vp != JSVAL_TRUE) {          /* Didn't match, so the string is unmodified. */
1949              /* Didn't match even once. */          *vp = STRING_TO_JSVAL(rdata.str);
1950              *vp = STRING_TO_JSVAL(rdata.base.str);          return true;
             goto out;  
         }  
         leftlen = cx->regExpStatics.leftContext.length;  
         ok = find_replen(cx, &rdata, &length);  
         if (!ok)  
             goto out;  
         length += leftlen;  
         chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));  
         if (!chars) {  
             ok = JS_FALSE;  
             goto out;  
         }  
         js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen);  
         do_replace(cx, &rdata, chars + leftlen);  
         rdata.chars = chars;  
         rdata.length = length;  
     }  
   
     rightlen = cx->regExpStatics.rightContext.length;  
     length = rdata.length + rightlen;  
     chars = (jschar *)  
         JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar));  
     if (!chars) {  
         JS_free(cx, rdata.chars);  
         ok = JS_FALSE;  
         goto out;  
     }  
     js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars,  
                rightlen);  
     chars[length] = 0;  
   
     str = js_NewString(cx, chars, length);  
     if (!str) {  
         JS_free(cx, chars);  
         ok = JS_FALSE;  
         goto out;  
1951      }      }
     *vp = STRING_TO_JSVAL(str);  
1952    
1953  out:      JSSubString *sub = &cx->regExpStatics.rightContext;
1954      /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */      if (!rdata.cb.append(sub->chars, sub->length))
1955      if (rdata.base.flags & KEEP_REGEXP)          return false;
1956          js_DestroyRegExp(cx, rdata.base.regexp);  
1957      return ok;      JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
1958        if (!retstr)
1959            return false;
1960    
1961        *vp = STRING_TO_JSVAL(retstr);
1962        return true;
1963  }  }
1964    
1965  /*  /*
# Line 1877  Line 1991 
1991       * limit argument (see str_split).       * limit argument (see str_split).
1992       */       */
1993      i = *ip;      i = *ip;
1994      length = JSSTRING_LENGTH(str);      length = str->length();
1995      if ((size_t)i > length)      if ((size_t)i > length)
1996          return -1;          return -1;
1997    
1998      chars = JSSTRING_CHARS(str);      chars = str->chars();
1999    
2000      /*      /*
2001       * Match a regular expression against the separator at or above index i.       * Match a regular expression against the separator at or above index i.
# Line 1908  Line 2022 
2022              /*              /*
2023               * Empty string match: never split on an empty match at the start               * Empty string match: never split on an empty match at the start
2024               * of a find_split cycle.  Same rule as for an empty global match               * of a find_split cycle.  Same rule as for an empty global match
2025               * in match_or_replace.               * in DoMatch.
2026               */               */
2027              if (i == *ip) {              if (i == *ip) {
2028                  /*                  /*
# Line 1982  Line 2096 
2096    
2097      if (argc == 0) {      if (argc == 0) {
2098          v = STRING_TO_JSVAL(str);          v = STRING_TO_JSVAL(str);
2099          ok = OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(0), &v);          ok = arrayobj->setProperty(cx, INT_TO_JSID(0), &v);
2100      } else {      } else {
2101          if (VALUE_IS_REGEXP(cx, vp[2])) {          if (VALUE_IS_REGEXP(cx, vp[2])) {
2102              re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(vp[2]));              re = (JSRegExp *) JSVAL_TO_OBJECT(vp[2])->getPrivate();
2103              sep = &tmp;              sep = &tmp;
2104    
2105              /* Set a magic value so we can detect a successful re match. */              /* Set a magic value so we can detect a successful re match. */
# Line 2001  Line 2115 
2115               * Point sep at a local copy of str2's header because find_split               * Point sep at a local copy of str2's header because find_split
2116               * will modify sep->length.               * will modify sep->length.
2117               */               */
2118              JSSTRING_CHARS_AND_LENGTH(str2, tmp.chars, tmp.length);              str2->getCharsAndLength(tmp.chars, tmp.length);
2119              sep = &tmp;              sep = &tmp;
2120              re = NULL;              re = NULL;
2121          }          }
# Line 2016  Line 2130 
2130    
2131              /* Clamp limit between 0 and 1 + string length. */              /* Clamp limit between 0 and 1 + string length. */
2132              limit = js_DoubleToECMAUint32(d);              limit = js_DoubleToECMAUint32(d);
2133              if (limit > JSSTRING_LENGTH(str))              if (limit > str->length())
2134                  limit = 1 + JSSTRING_LENGTH(str);                  limit = 1 + str->length();
2135          }          }
2136    
2137          len = i = 0;          len = i = 0;
# Line 2075  Line 2189 
2189          d = js_ValueToNumber(cx, &vp[2]);          d = js_ValueToNumber(cx, &vp[2]);
2190          if (JSVAL_IS_NULL(vp[2]))          if (JSVAL_IS_NULL(vp[2]))
2191              return JS_FALSE;              return JS_FALSE;
2192          length = JSSTRING_LENGTH(str);          length = str->length();
2193          begin = js_DoubleToInteger(d);          begin = js_DoubleToInteger(d);
2194          if (begin < 0) {          if (begin < 0) {
2195              begin += length;              begin += length;
# Line 2153  Line 2267 
2267    
2268          str = JSVAL_TO_STRING(t);          str = JSVAL_TO_STRING(t);
2269          begin = JSVAL_TO_INT(v);          begin = JSVAL_TO_INT(v);
2270          end = JSSTRING_LENGTH(str);          end = str->length();
2271          if (begin <= end) {          if (begin <= end) {
2272              length = end - begin;              length = end - begin;
2273              if (length == 0) {              if (length == 0) {
2274                  str = cx->runtime->emptyString;                  str = cx->runtime->emptyString;
2275              } else {              } else {
2276                  str = (length == 1)                  str = (length == 1)
2277                        ? js_GetUnitString(cx, str, begin)                        ? JSString::getUnitString(cx, str, begin)
2278                        : js_NewDependentString(cx, str, begin, length);                        : js_NewDependentString(cx, str, begin, length);
2279                  if (!str)                  if (!str)
2280                      return JS_FALSE;                      return JS_FALSE;
# Line 2179  Line 2293 
2293          if (JSVAL_IS_NULL(vp[2]))          if (JSVAL_IS_NULL(vp[2]))
2294              return JS_FALSE;              return JS_FALSE;
2295          begin = js_DoubleToInteger(begin);          begin = js_DoubleToInteger(begin);
2296          length = JSSTRING_LENGTH(str);          length = str->length();
2297          if (begin < 0) {          if (begin < 0) {
2298              begin += length;              begin += length;
2299              if (begin < 0)              if (begin < 0)
# Line 2238  Line 2352 
2352      taglen = 1 + beglen + 1;                            /* '<begin' + '>' */      taglen = 1 + beglen + 1;                            /* '<begin' + '>' */
2353      parlen = 0; /* Avoid warning. */      parlen = 0; /* Avoid warning. */
2354      if (param) {      if (param) {
2355          parlen = JSSTRING_LENGTH(param);          parlen = param->length();
2356          taglen += 2 + parlen + 1;                       /* '="param"' */          taglen += 2 + parlen + 1;                       /* '="param"' */
2357      }      }
2358      endlen = strlen(end);      endlen = strlen(end);
2359      taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1;    /* 'str</end>' */      taglen += str->length() + 2 + endlen + 1;    /* 'str</end>' */
2360    
2361      if (taglen >= ~(size_t)0 / sizeof(jschar)) {      if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2362          js_ReportAllocationOverflow(cx);          js_ReportAllocationOverflow(cx);
2363          return JS_FALSE;          return JS_FALSE;
2364      }      }
2365    
2366      tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar));      tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2367      if (!tagbuf)      if (!tagbuf)
2368          return JS_FALSE;          return JS_FALSE;
2369    
# Line 2260  Line 2374 
2374      if (param) {      if (param) {
2375          tagbuf[j++] = '=';          tagbuf[j++] = '=';
2376          tagbuf[j++] = '"';          tagbuf[j++] = '"';
2377          js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen);          js_strncpy(&tagbuf[j], param->chars(), parlen);
2378          j += parlen;          j += parlen;
2379          tagbuf[j++] = '"';          tagbuf[j++] = '"';
2380      }      }
2381      tagbuf[j++] = '>';      tagbuf[j++] = '>';
2382      js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str));      js_strncpy(&tagbuf[j], str->chars(), str->length());
2383      j += JSSTRING_LENGTH(str);      j += str->length();
2384      tagbuf[j++] = '<';      tagbuf[j++] = '<';
2385      tagbuf[j++] = '/';      tagbuf[j++] = '/';
2386      for (i = 0; i < endlen; i++)      for (i = 0; i < endlen; i++)
# Line 2277  Line 2391 
2391    
2392      str = js_NewString(cx, tagbuf, taglen);      str = js_NewString(cx, tagbuf, taglen);
2393      if (!str) {      if (!str) {
2394          free((char *)tagbuf);          js_free((char *)tagbuf);
2395          return JS_FALSE;          return JS_FALSE;
2396      }      }
2397      *vp = STRING_TO_JSVAL(str);      *vp = STRING_TO_JSVAL(str);
# Line 2379  Line 2493 
2493  JSString* FASTCALL  JSString* FASTCALL
2494  js_String_getelem(JSContext* cx, JSString* str, int32 i)  js_String_getelem(JSContext* cx, JSString* str, int32 i)
2495  {  {
2496      if ((size_t)i >= JSSTRING_LENGTH(str))      if ((size_t)i >= str->length())
2497          return NULL;          return NULL;
2498      return js_GetUnitString(cx, str, (size_t)i);      return JSString::getUnitString(cx, str, size_t(i));
2499  }  }
2500  #endif  #endif
2501    
2502  JS_DEFINE_CALLINFO_2(extern, BOOL,   js_EqualStrings, STRING, STRING,                       1, 1)  JS_DEFINE_TRCINFO_1(js_str_toString,
 JS_DEFINE_CALLINFO_2(extern, INT32,  js_CompareStrings, STRING, STRING,                     1, 1)  
   
 JS_DEFINE_TRCINFO_1(str_toString,  
2503      (2, (extern, STRING_RETRY,      String_p_toString, CONTEXT, THIS,                        1, 1)))      (2, (extern, STRING_RETRY,      String_p_toString, CONTEXT, THIS,                        1, 1)))
2504  JS_DEFINE_TRCINFO_1(str_charAt,  JS_DEFINE_TRCINFO_1(str_charAt,
2505      (3, (extern, STRING_RETRY,      js_String_getelem, CONTEXT, THIS_STRING, INT32,           1, 1)))      (3, (extern, STRING_RETRY,      js_String_getelem, CONTEXT, THIS_STRING, INT32,           1, 1)))
# Line 2409  Line 2520 
2520  #endif  #endif
2521    
2522      /* Java-like methods. */      /* Java-like methods. */
2523      JS_TN(js_toString_str,     str_toString,          0,JSFUN_THISP_STRING, str_toString_trcinfo),      JS_TN(js_toString_str,     js_str_toString,       0,JSFUN_THISP_STRING, &js_str_toString_trcinfo),
2524      JS_FN(js_valueOf_str,      str_toString,          0,JSFUN_THISP_STRING),      JS_FN(js_valueOf_str,      js_str_toString,       0,JSFUN_THISP_STRING),
2525      JS_FN(js_toJSON_str,       str_toString,          0,JSFUN_THISP_STRING),      JS_FN(js_toJSON_str,       js_str_toString,       0,JSFUN_THISP_STRING),
2526      JS_FN("substring",         str_substring,         2,GENERIC_PRIMITIVE),      JS_FN("substring",         str_substring,         2,GENERIC_PRIMITIVE),
2527      JS_FN("toLowerCase",       str_toLowerCase,       0,GENERIC_PRIMITIVE),      JS_FN("toLowerCase",       str_toLowerCase,       0,GENERIC_PRIMITIVE),
2528      JS_FN("toUpperCase",       str_toUpperCase,       0,GENERIC_PRIMITIVE),      JS_FN("toUpperCase",       str_toUpperCase,       0,GENERIC_PRIMITIVE),
2529      JS_TN("charAt",            str_charAt,            1,GENERIC_PRIMITIVE, str_charAt_trcinfo),      JS_TN("charAt",            str_charAt,            1,GENERIC_PRIMITIVE, &str_charAt_trcinfo),
2530      JS_TN("charCodeAt",        str_charCodeAt,        1,GENERIC_PRIMITIVE, str_charCodeAt_trcinfo),      JS_TN("charCodeAt",        str_charCodeAt,        1,GENERIC_PRIMITIVE, &str_charCodeAt_trcinfo),
2531      JS_FN("indexOf",           str_indexOf,           1,GENERIC_PRIMITIVE),      JS_FN("indexOf",           str_indexOf,           1,GENERIC_PRIMITIVE),
2532      JS_FN("lastIndexOf",       str_lastIndexOf,       1,GENERIC_PRIMITIVE),      JS_FN("lastIndexOf",       str_lastIndexOf,       1,GENERIC_PRIMITIVE),
2533      JS_FN("trim",              str_trim,              0,GENERIC_PRIMITIVE),      JS_FN("trim",              str_trim,              0,GENERIC_PRIMITIVE),
# Line 2436  Line 2547 
2547  #endif  #endif
2548    
2549      /* Python-esque sequence methods. */      /* Python-esque sequence methods. */
2550      JS_TN("concat",            str_concat,            1,GENERIC_PRIMITIVE, str_concat_trcinfo),      JS_TN("concat",            str_concat,            1,GENERIC_PRIMITIVE, &str_concat_trcinfo),
2551      JS_FN("slice",             str_slice,             2,GENERIC_PRIMITIVE),      JS_FN("slice",             str_slice,             2,GENERIC_PRIMITIVE),
2552    
2553      /* HTML string methods. */      /* HTML string methods. */
# Line 2459  Line 2570 
2570      JS_FS_END      JS_FS_END
2571  };  };
2572    
2573    #define C(c) c, 0x00
2574    
2575    /*
2576     * String data for all unit strings (including zero-char backstop required for independent strings),
2577     * packed into single array.
2578     */
2579    static const jschar UnitStringData[] = {
2580        C(0x00), C(0x01), C(0x02), C(0x03), C(0x04), C(0x05), C(0x06), C(0x07),
2581        C(0x08), C(0x09), C(0x0a), C(0x0b), C(0x0c), C(0x0d), C(0x0e), C(0x0f),
2582        C(0x10), C(0x11), C(0x12), C(0x13), C(0x14), C(0x15), C(0x16), C(0x17),
2583        C(0x18), C(0x19), C(0x1a), C(0x1b), C(0x1c), C(0x1d), C(0x1e), C(0x1f),
2584        C(0x20), C(0x21), C(0x22), C(0x23), C(0x24), C(0x25), C(0x26), C(0x27),
2585        C(0x28), C(0x29), C(0x2a), C(0x2b), C(0x2c), C(0x2d), C(0x2e), C(0x2f),
2586        C(0x30), C(0x31), C(0x32), C(0x33), C(0x34), C(0x35), C(0x36), C(0x37),
2587        C(0x38), C(0x39), C(0x3a), C(0x3b), C(0x3c), C(0x3d), C(0x3e), C(0x3f),
2588        C(0x40), C(0x41), C(0x42), C(0x43), C(0x44), C(0x45), C(0x46), C(0x47),
2589        C(0x48), C(0x49), C(0x4a), C(0x4b), C(0x4c), C(0x4d), C(0x4e), C(0x4f),
2590        C(0x50), C(0x51), C(0x52), C(0x53), C(0x54), C(0x55), C(0x56), C(0x57),
2591        C(0x58), C(0x59), C(0x5a), C(0x5b), C(0x5c), C(0x5d), C(0x5e), C(0x5f),
2592        C(0x60), C(0x61), C(0x62), C(0x63), C(0x64), C(0x65), C(0x66), C(0x67),
2593        C(0x68), C(0x69), C(0x6a), C(0x6b), C(0x6c), C(0x6d), C(0x6e), C(0x6f),
2594        C(0x70), C(0x71), C(0x72), C(0x73), C(0x74), C(0x75), C(0x76), C(0x77),
2595        C(0x78), C(0x79), C(0x7a), C(0x7b), C(0x7c), C(0x7d), C(0x7e), C(0x7f),
2596        C(0x80), C(0x81), C(0x82), C(0x83), C(0x84), C(0x85), C(0x86), C(0x87),
2597        C(0x88), C(0x89), C(0x8a), C(0x8b), C(0x8c), C(0x8d), C(0x8e), C(0x8f),
2598        C(0x90), C(0x91), C(0x92), C(0x93), C(0x94), C(0x95), C(0x96), C(0x97),
2599        C(0x98), C(0x99), C(0x9a), C(0x9b), C(0x9c), C(0x9d), C(0x9e), C(0x9f),
2600        C(0xa0), C(0xa1), C(0xa2), C(0xa3), C(0xa4), C(0xa5), C(0xa6), C(0xa7),
2601        C(0xa8), C(0xa9), C(0xaa), C(0xab), C(0xac), C(0xad), C(0xae), C(0xaf),
2602        C(0xb0), C(0xb1), C(0xb2), C(0xb3), C(0xb4), C(0xb5), C(0xb6), C(0xb7),
2603        C(0xb8), C(0xb9), C(0xba), C(0xbb), C(0xbc), C(0xbd), C(0xbe), C(0xbf),
2604        C(0xc0), C(0xc1), C(0xc2), C(0xc3), C(0xc4), C(0xc5), C(0xc6), C(0xc7),
2605        C(0xc8), C(0xc9), C(0xca), C(0xcb), C(0xcc), C(0xcd), C(0xce), C(0xcf),
2606        C(0xd0), C(0xd1), C(0xd2), C(0xd3), C(0xd4), C(0xd5), C(0xd6), C(0xd7),
2607        C(0xd8), C(0xd9), C(0xda), C(0xdb), C(0xdc), C(0xdd), C(0xde), C(0xdf),
2608        C(0xe0), C(0xe1), C(0xe2), C(0xe3), C(0xe4), C(0xe5), C(0xe6), C(0xe7),
2609        C(0xe8), C(0xe9), C(0xea), C(0xeb), C(0xec), C(0xed), C(0xee), C(0xef),
2610        C(0xf0), C(0xf1), C(0xf2), C(0xf3), C(0xf4), C(0xf5), C(0xf6), C(0xf7),
2611        C(0xf8), C(0xf9), C(0xfa), C(0xfb), C(0xfc), C(0xfd), C(0xfe), C(0xff)
2612    };
2613    
2614    #define U(c) { 1 | JSString::ATOMIZED, {(jschar *)UnitStringData + (c) * 2} }
2615    
2616    #ifdef __SUNPRO_CC
2617    #pragma pack(8)
2618    #else
2619    #pragma pack(push, 8)
2620    #endif
2621    
2622    JSString JSString::unitStringTable[]
2623    #ifdef __GNUC__
2624    __attribute__ ((aligned (8)))
2625    #endif
2626    = {
2627        U(0x00), U(0x01), U(0x02), U(0x03), U(0x04), U(0x05), U(0x06), U(0x07),
2628        U(0x08), U(0x09), U(0x0a), U(0x0b), U(0x0c), U(0x0d), U(0x0e), U(0x0f),
2629        U(0x10), U(0x11), U(0x12), U(0x13), U(0x14), U(0x15), U(0x16), U(0x17),
2630        U(0x18), U(0x19), U(0x1a), U(0x1b), U(0x1c), U(0x1d), U(0x1e), U(0x1f),
2631        U(0x20), U(0x21), U(0x22), U(0x23), U(0x24), U(0x25), U(0x26), U(0x27),
2632        U(0x28), U(0x29), U(0x2a), U(0x2b), U(0x2c), U(0x2d), U(0x2e), U(0x2f),
2633        U(0x30), U(0x31), U(0x32), U(0x33), U(0x34), U(0x35), U(0x36), U(0x37),
2634        U(0x38), U(0x39), U(0x3a), U(0x3b), U(0x3c), U(0x3d), U(0x3e), U(0x3f),
2635        U(0x40), U(0x41), U(0x42), U(0x43), U(0x44), U(0x45), U(0x46), U(0x47),
2636        U(0x48), U(0x49), U(0x4a), U(0x4b), U(0x4c), U(0x4d), U(0x4e), U(0x4f),
2637        U(0x50), U(0x51), U(0x52), U(0x53), U(0x54), U(0x55), U(0x56), U(0x57),
2638        U(0x58), U(0x59), U(0x5a), U(0x5b), U(0x5c), U(0x5d), U(0x5e), U(0x5f),
2639        U(0x60), U(0x61), U(0x62), U(0x63), U(0x64), U(0x65), U(0x66), U(0x67),
2640        U(0x68), U(0x69), U(0x6a), U(0x6b), U(0x6c), U(0x6d), U(0x6e), U(0x6f),
2641        U(0x70), U(0x71), U(0x72), U(0x73), U(0x74), U(0x75), U(0x76), U(0x77),
2642        U(0x78), U(0x79), U(0x7a), U(0x7b), U(0x7c), U(0x7d), U(0x7e), U(0x7f),
2643        U(0x80), U(0x81), U(0x82), U(0x83), U(0x84), U(0x85), U(0x86), U(0x87),
2644        U(0x88), U(0x89), U(0x8a), U(0x8b), U(0x8c), U(0x8d), U(0x8e), U(0x8f),
2645        U(0x90), U(0x91), U(0x92), U(0x93), U(0x94), U(0x95), U(0x96), U(0x97),
2646        U(0x98), U(0x99), U(0x9a), U(0x9b), U(0x9c), U(0x9d), U(0x9e), U(0x9f),
2647        U(0xa0), U(0xa1), U(0xa2), U(0xa3), U(0xa4), U(0xa5), U(0xa6), U(0xa7),
2648        U(0xa8), U(0xa9), U(0xaa), U(0xab), U(0xac), U(0xad), U(0xae), U(0xaf),
2649        U(0xb0), U(0xb1), U(0xb2), U(0xb3), U(0xb4), U(0xb5), U(0xb6), U(0xb7),
2650        U(0xb8), U(0xb9), U(0xba), U(0xbb), U(0xbc), U(0xbd), U(0xbe), U(0xbf),
2651        U(0xc0), U(0xc1), U(0xc2), U(0xc3), U(0xc4), U(0xc5), U(0xc6), U(0xc7),
2652        U(0xc8), U(0xc9), U(0xca), U(0xcb), U(0xcc), U(0xcd), U(0xce), U(0xcf),
2653        U(0xd0), U(0xd1), U(0xd2), U(0xd3), U(0xd4), U(0xd5), U(0xd6), U(0xd7),
2654        U(0xd8), U(0xd9), U(0xda), U(0xdb), U(0xdc), U(0xdd), U(0xde), U(0xdf),
2655        U(0xe0), U(0xe1), U(0xe2), U(0xe3), U(0xe4), U(0xe5), U(0xe6), U(0xe7),
2656        U(0xe8), U(0xe9), U(0xea), U(0xeb), U(0xec), U(0xed), U(0xee), U(0xef),
2657        U(0xf0), U(0xf1), U(0xf2), U(0xf3), U(0xf4), U(0xf5), U(0xf6), U(0xf7),
2658        U(0xf8), U(0xf9), U(0xfa), U(0xfb), U(0xfc), U(0xfd), U(0xfe), U(0xff)
2659    };
2660    
2661    #ifdef __SUNPRO_CC
2662    #pragma pack(0)
2663    #else
2664    #pragma pack(pop)
2665    #endif
2666    
2667    #undef U
2668    
2669    #define O0(c) 0x30, C(c)
2670    #define O1(c) 0x31, C(c) /* template for 10 .. 19 */
2671    #define O2(c) 0x32, C(c) /* template for 20 .. 29 */
2672    #define O3(c) 0x33, C(c)
2673    #define O4(c) 0x34, C(c)
2674    #define O5(c) 0x35, C(c)
2675    #define O6(c) 0x36, C(c)
2676    #define O7(c) 0x37, C(c)
2677    #define O8(c) 0x38, C(c)
2678    #define O9(c) 0x39, C(c)
2679    
2680    #define O10(c) 0x31, O0(c) /* template for 100 .. 109 */
2681    #define O11(c) 0x31, O1(c) /* template for 110 .. 119 */
2682    #define O12(c) 0x31, O2(c)
2683    #define O13(c) 0x31, O3(c)
2684    #define O14(c) 0x31, O4(c)
2685    #define O15(c) 0x31, O5(c)
2686    #define O16(c) 0x31, O6(c)
2687    #define O17(c) 0x31, O7(c)
2688    #define O18(c) 0x31, O8(c)
2689    #define O19(c) 0x31, O9(c)
2690    #define O20(c) 0x32, O0(c)
2691    #define O21(c) 0x32, O1(c)
2692    #define O22(c) 0x32, O2(c)
2693    #define O23(c) 0x32, O3(c)
2694    #define O24(c) 0x32, O4(c)
2695    #define O25(c) 0x32, O5(c)
2696    
2697    /*
2698     * Array starts with 100, 101, 102... (0x31 0x30 0x30 0x00 for 100\0)
2699     * 100, 101, 102 also share the pointers to 0, 1, 2 ...
2700     * 110, 111, 112 also share the pointers to 10, 11, 12...
2701     */
2702    static const jschar Hundreds[] = {
2703        O10(0x30), O10(0x31), O10(0x32), O10(0x33), O10(0x34), O10(0x35), O10(0x36), O10(0x37), O10(0x38), O10(0x39),
2704        O11(0x30), O11(0x31), O11(0x32), O11(0x33), O11(0x34), O11(0x35), O11(0x36), O11(0x37), O11(0x38), O11(0x39),
2705        O12(0x30), O12(0x31), O12(0x32), O12(0x33), O12(0x34), O12(0x35), O12(0x36), O12(0x37), O12(0x38), O12(0x39),
2706        O13(0x30), O13(0x31), O13(0x32), O13(0x33), O13(0x34), O13(0x35), O13(0x36), O13(0x37), O13(0x38), O13(0x39),
2707        O14(0x30), O14(0x31), O14(0x32), O14(0x33), O14(0x34), O14(0x35), O14(0x36), O14(0x37), O14(0x38), O14(0x39),
2708        O15(0x30), O15(0x31), O15(0x32), O15(0x33), O15(0x34), O15(0x35), O15(0x36), O15(0x37), O15(0x38), O15(0x39),
2709        O16(0x30), O16(0x31), O16(0x32), O16(0x33), O16(0x34), O16(0x35), O16(0x36), O16(0x37), O16(0x38), O16(0x39),
2710        O17(0x30), O17(0x31), O17(0x32), O17(0x33), O17(0x34), O17(0x35), O17(0x36), O17(0x37), O17(0x38), O17(0x39),
2711        O18(0x30), O18(0x31), O18(0x32), O18(0x33), O18(0x34), O18(0x35), O18(0x36), O18(0x37), O18(0x38), O18(0x39),
2712        O19(0x30), O19(0x31), O19(0x32), O19(0x33), O19(0x34), O19(0x35), O19(0x36), O19(0x37), O19(0x38), O19(0x39),
2713        O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39),
2714        O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39),
2715        O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39),
2716        O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23(0x37), O23(0x38), O23(0x39),
2717        O24(0x30), O24(0x31), O24(0x32), O24(0x33), O24(0x34), O24(0x35), O24(0x36), O24(0x37), O24(0x38), O24(0x39),
2718        O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35)
2719    };
2720    
2721    #define L1(c) { 1 | JSString::ATOMIZED, {(jschar *)Hundreds + 2 + (c) * 4} } /* length 1: 0..9 */
2722    #define L2(c) { 2 | JSString::ATOMIZED, {(jschar *)Hundreds + 41 + (c - 10) * 4} } /* length 2: 10..99 */
2723    #define L3(c) { 3 | JSString::ATOMIZED, {(jschar *)Hundreds + (c - 100) * 4} } /* length 3: 100..255 */
2724    
2725    #ifdef __SUNPRO_CC
2726    #pragma pack(8)
2727    #else
2728    #pragma pack(push, 8)
2729    #endif
2730    
2731    JSString JSString::intStringTable[]
2732    #ifdef __GNUC__
2733    __attribute__ ((aligned (8)))
2734    #endif
2735    = {
2736        L1(0x00), L1(0x01), L1(0x02), L1(0x03), L1(0x04), L1(0x05), L1(0x06), L1(0x07),
2737        L1(0x08), L1(0x09), L2(0x0a), L2(0x0b), L2(0x0c), L2(0x0d), L2(0x0e), L2(0x0f),
2738        L2(0x10), L2(0x11), L2(0x12), L2(0x13), L2(0x14), L2(0x15), L2(0x16), L2(0x17),
2739        L2(0x18), L2(0x19), L2(0x1a), L2(0x1b), L2(0x1c), L2(0x1d), L2(0x1e), L2(0x1f),
2740        L2(0x20), L2(0x21), L2(0x22), L2(0x23), L2(0x24), L2(0x25), L2(0x26), L2(0x27),
2741        L2(0x28), L2(0x29), L2(0x2a), L2(0x2b), L2(0x2c), L2(0x2d), L2(0x2e), L2(0x2f),
2742        L2(0x30), L2(0x31), L2(0x32), L2(0x33), L2(0x34), L2(0x35), L2(0x36), L2(0x37),
2743        L2(0x38), L2(0x39), L2(0x3a), L2(0x3b), L2(0x3c), L2(0x3d), L2(0x3e), L2(0x3f),
2744        L2(0x40), L2(0x41), L2(0x42), L2(0x43), L2(0x44), L2(0x45), L2(0x46), L2(0x47),
2745        L2(0x48), L2(0x49), L2(0x4a), L2(0x4b), L2(0x4c), L2(0x4d), L2(0x4e), L2(0x4f),
2746        L2(0x50), L2(0x51), L2(0x52), L2(0x53), L2(0x54), L2(0x55), L2(0x56), L2(0x57),
2747        L2(0x58), L2(0x59), L2(0x5a), L2(0x5b), L2(0x5c), L2(0x5d), L2(0x5e), L2(0x5f),
2748        L2(0x60), L2(0x61), L2(0x62), L2(0x63), L3(0x64), L3(0x65), L3(0x66), L3(0x67),
2749        L3(0x68), L3(0x69), L3(0x6a), L3(0x6b), L3(0x6c), L3(0x6d), L3(0x6e), L3(0x6f),
2750        L3(0x70), L3(0x71), L3(0x72), L3(0x73), L3(0x74), L3(0x75), L3(0x76), L3(0x77),
2751        L3(0x78), L3(0x79), L3(0x7a), L3(0x7b), L3(0x7c), L3(0x7d), L3(0x7e), L3(0x7f),
2752        L3(0x80), L3(0x81), L3(0x82), L3(0x83), L3(0x84), L3(0x85), L3(0x86), L3(0x87),
2753        L3(0x88), L3(0x89), L3(0x8a), L3(0x8b), L3(0x8c), L3(0x8d), L3(0x8e), L3(0x8f),
2754        L3(0x90), L3(0x91), L3(0x92), L3(0x93), L3(0x94), L3(0x95), L3(0x96), L3(0x97),
2755        L3(0x98), L3(0x99), L3(0x9a), L3(0x9b), L3(0x9c), L3(0x9d), L3(0x9e), L3(0x9f),
2756        L3(0xa0), L3(0xa1), L3(0xa2), L3(0xa3), L3(0xa4), L3(0xa5), L3(0xa6), L3(0xa7),
2757        L3(0xa8), L3(0xa9), L3(0xaa), L3(0xab), L3(0xac), L3(0xad), L3(0xae), L3(0xaf),
2758        L3(0xb0), L3(0xb1), L3(0xb2), L3(0xb3), L3(0xb4), L3(0xb5), L3(0xb6), L3(0xb7),
2759        L3(0xb8), L3(0xb9), L3(0xba), L3(0xbb), L3(0xbc), L3(0xbd), L3(0xbe), L3(0xbf),
2760        L3(0xc0), L3(0xc1), L3(0xc2), L3(0xc3), L3(0xc4), L3(0xc5), L3(0xc6), L3(0xc7),
2761        L3(0xc8), L3(0xc9), L3(0xca), L3(0xcb), L3(0xcc), L3(0xcd), L3(0xce), L3(0xcf),
2762        L3(0xd0), L3(0xd1), L3(0xd2), L3(0xd3), L3(0xd4), L3(0xd5), L3(0xd6), L3(0xd7),
2763        L3(0xd8), L3(0xd9), L3(0xda), L3(0xdb), L3(0xdc), L3(0xdd), L3(0xde), L3(0xdf),
2764        L3(0xe0), L3(0xe1), L3(0xe2), L3(0xe3), L3(0xe4), L3(0xe5), L3(0xe6), L3(0xe7),
2765        L3(0xe8), L3(0xe9), L3(0xea), L3(0xeb), L3(0xec), L3(0xed), L3(0xee), L3(0xef),
2766        L3(0xf0), L3(0xf1), L3(0xf2), L3(0xf3), L3(0xf4), L3(0xf5), L3(0xf6), L3(0xf7),
2767        L3(0xf8), L3(0xf9), L3(0xfa), L3(0xfb), L3(0xfc), L3(0xfd), L3(0xfe), L3(0xff)
2768    };
2769    
2770    #ifdef __SUNPRO_CC
2771    #pragma pack(0)
2772    #else
2773    #pragma pack(pop)
2774    #endif
2775    
2776    #undef L1
2777    #undef L2
2778    #undef L3
2779    
2780    static const char AsciiHundreds[] = {
2781        O10(0x30), O10(0x31), O10(0x32), O10(0x33), O10(0x34), O10(0x35), O10(0x36), O10(0x37), O10(0x38), O10(0x39),
2782        O11(0x30), O11(0x31), O11(0x32), O11(0x33), O11(0x34), O11(0x35), O11(0x36), O11(0x37), O11(0x38), O11(0x39),
2783        O12(0x30), O12(0x31), O12(0x32), O12(0x33), O12(0x34), O12(0x35), O12(0x36), O12(0x37), O12(0x38), O12(0x39),
2784        O13(0x30), O13(0x31), O13(0x32), O13(0x33), O13(0x34), O13(0x35), O13(0x36), O13(0x37), O13(0x38), O13(0x39),
2785        O14(0x30), O14(0x31), O14(0x32), O14(0x33), O14(0x34), O14(0x35), O14(0x36), O14(0x37), O14(0x38), O14(0x39),
2786        O15(0x30), O15(0x31), O15(0x32), O15(0x33), O15(0x34), O15(0x35), O15(0x36), O15(0x37), O15(0x38), O15(0x39),
2787        O16(0x30), O16(0x31), O16(0x32), O16(0x33), O16(0x34), O16(0x35), O16(0x36), O16(0x37), O16(0x38), O16(0x39),
2788        O17(0x30), O17(0x31), O17(0x32), O17(0x33), O17(0x34), O17(0x35), O17(0x36), O17(0x37), O17(0x38), O17(0x39),
2789        O18(0x30), O18(0x31), O18(0x32), O18(0x33), O18(0x34), O18(0x35), O18(0x36), O18(0x37), O18(0x38), O18(0x39),
2790        O19(0x30), O19(0x31), O19(0x32), O19(0x33), O19(0x34), O19(0x35), O19(0x36), O19(0x37), O19(0x38), O19(0x39),
2791        O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39),
2792        O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39),
2793        O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39),
2794        O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23(0x37), O23(0x38), O23(0x39),
2795        O24(0x30), O24(0x31), O24(0x32), O24(0x33), O24(0x34), O24(0x35), O24(0x36), O24(0x37), O24(0x38), O24(0x39),
2796        O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35)
2797    };
2798    
2799    #define L1(c) (AsciiHundreds + 2 + (c) * 4)             /* length 1: 0..9 */
2800    #define L2(c) (AsciiHundreds + 41 + (c - 10) * 4)       /* length 2: 10..99 */
2801    #define L3(c) (AsciiHundreds + (c - 100) * 4)           /* length 3: 100..255 */
2802    
2803    const char *JSString::deflatedIntStringTable[] = {
2804        L1(0x00), L1(0x01), L1(0x02), L1(0x03), L1(0x04), L1(0x05), L1(0x06), L1(0x07),
2805        L1(0x08), L1(0x09), L2(0x0a), L2(0x0b), L2(0x0c), L2(0x0d), L2(0x0e), L2(0x0f),
2806        L2(0x10), L2(0x11), L2(0x12), L2(0x13), L2(0x14), L2(0x15), L2(0x16), L2(0x17),
2807        L2(0x18), L2(0x19), L2(0x1a), L2(0x1b), L2(0x1c), L2(0x1d), L2(0x1e), L2(0x1f),
2808        L2(0x20), L2(0x21), L2(0x22), L2(0x23), L2(0x24), L2(0x25), L2(0x26), L2(0x27),
2809        L2(0x28), L2(0x29), L2(0x2a), L2(0x2b), L2(0x2c), L2(0x2d), L2(0x2e), L2(0x2f),
2810        L2(0x30), L2(0x31), L2(0x32), L2(0x33), L2(0x34), L2(0x35), L2(0x36), L2(0x37),
2811        L2(0x38), L2(0x39), L2(0x3a), L2(0x3b), L2(0x3c), L2(0x3d), L2(0x3e), L2(0x3f),
2812        L2(0x40), L2(0x41), L2(0x42), L2(0x43), L2(0x44), L2(0x45), L2(0x46), L2(0x47),
2813        L2(0x48), L2(0x49), L2(0x4a), L2(0x4b), L2(0x4c), L2(0x4d), L2(0x4e), L2(0x4f),
2814        L2(0x50), L2(0x51), L2(0x52), L2(0x53), L2(0x54), L2(0x55), L2(0x56), L2(0x57),
2815        L2(0x58), L2(0x59), L2(0x5a), L2(0x5b), L2(0x5c), L2(0x5d), L2(0x5e), L2(0x5f),
2816        L2(0x60), L2(0x61), L2(0x62), L2(0x63), L3(0x64), L3(0x65), L3(0x66), L3(0x67),
2817        L3(0x68), L3(0x69), L3(0x6a), L3(0x6b), L3(0x6c), L3(0x6d), L3(0x6e), L3(0x6f),
2818        L3(0x70), L3(0x71), L3(0x72), L3(0x73), L3(0x74), L3(0x75), L3(0x76), L3(0x77),
2819        L3(0x78), L3(0x79), L3(0x7a), L3(0x7b), L3(0x7c), L3(0x7d), L3(0x7e), L3(0x7f),
2820        L3(0x80), L3(0x81), L3(0x82), L3(0x83), L3(0x84), L3(0x85), L3(0x86), L3(0x87),
2821        L3(0x88), L3(0x89), L3(0x8a), L3(0x8b), L3(0x8c), L3(0x8d), L3(0x8e), L3(0x8f),
2822        L3(0x90), L3(0x91), L3(0x92), L3(0x93), L3(0x94), L3(0x95), L3(0x96), L3(0x97),
2823        L3(0x98), L3(0x99), L3(0x9a), L3(0x9b), L3(0x9c), L3(0x9d), L3(0x9e), L3(0x9f),
2824        L3(0xa0), L3(0xa1), L3(0xa2), L3(0xa3), L3(0xa4), L3(0xa5), L3(0xa6), L3(0xa7),
2825        L3(0xa8), L3(0xa9), L3(0xaa), L3(0xab), L3(0xac), L3(0xad), L3(0xae), L3(0xaf),
2826        L3(0xb0), L3(0xb1), L3(0xb2), L3(0xb3), L3(0xb4), L3(0xb5), L3(0xb6), L3(0xb7),
2827        L3(0xb8), L3(0xb9), L3(0xba), L3(0xbb), L3(0xbc), L3(0xbd), L3(0xbe), L3(0xbf),
2828        L3(0xc0), L3(0xc1), L3(0xc2), L3(0xc3), L3(0xc4), L3(0xc5), L3(0xc6), L3(0xc7),
2829        L3(0xc8), L3(0xc9), L3(0xca), L3(0xcb), L3(0xcc), L3(0xcd), L3(0xce), L3(0xcf),
2830        L3(0xd0), L3(0xd1), L3(0xd2), L3(0xd3), L3(0xd4), L3(0xd5), L3(0xd6), L3(0xd7),
2831        L3(0xd8), L3(0xd9), L3(0xda), L3(0xdb), L3(0xdc), L3(0xdd), L3(0xde), L3(0xdf),
2832        L3(0xe0), L3(0xe1), L3(0xe2), L3(0xe3), L3(0xe4), L3(0xe5), L3(0xe6), L3(0xe7),
2833        L3(0xe8), L3(0xe9), L3(0xea), L3(0xeb), L3(0xec), L3(0xed), L3(0xee), L3(0xef),
2834        L3(0xf0), L3(0xf1), L3(0xf2), L3(0xf3), L3(0xf4), L3(0xf5), L3(0xf6), L3(0xf7),
2835        L3(0xf8), L3(0xf9), L3(0xfa), L3(0xfb), L3(0xfc), L3(0xfd), L3(0xfe), L3(0xff)
2836    };
2837    
2838    #undef L1
2839    #undef L2
2840    #undef L3
2841    
2842    #undef C
2843    
2844    #undef O0
2845    #undef O1
2846    #undef O2
2847    #undef O3
2848    #undef O4
2849    #undef O5
2850    #undef O6
2851    #undef O7
2852    #undef O8
2853    #undef O9
2854    #undef O10
2855    #undef O11
2856    #undef O12
2857    #undef O13
2858    #undef O14
2859    #undef O15
2860    #undef O16
2861    #undef O17
2862    #undef O18
2863    #undef O19
2864    #undef O20
2865    #undef O21
2866    #undef O22
2867    #undef O23
2868    #undef O24
2869    #undef O25
2870    
2871  JSBool  JSBool
2872  js_String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)  js_String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2873  {  {
# Line 2476  Line 2885 
2885          *rval = STRING_TO_JSVAL(str);          *rval = STRING_TO_JSVAL(str);
2886          return JS_TRUE;          return JS_TRUE;
2887      }      }
2888      obj->fslots[JSSLOT_PRIVATE] = STRING_TO_JSVAL(str);      obj->fslots[JSSLOT_PRIMITIVE_THIS] = STRING_TO_JSVAL(str);
2889      return JS_TRUE;      return JS_TRUE;
2890  }  }
2891    
# Line 2486  Line 2895 
2895  js_String_tn(JSContext* cx, JSObject* proto, JSString* str)  js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
2896  {  {
2897      JS_ASSERT(JS_ON_TRACE(cx));      JS_ASSERT(JS_ON_TRACE(cx));
2898      JSObject* obj = js_NewNativeObject(cx, &js_StringClass, proto, JSSLOT_PRIVATE + 1);      return js_NewObjectWithClassProto(cx, &js_StringClass, proto, STRING_TO_JSVAL(str));
     if (!obj)  
         return NULL;  
   
     obj->fslots[JSSLOT_PRIVATE] = STRING_TO_JSVAL(str);  
     return obj;  
2899  }  }
   
2900  JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, 0)  JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, 0)
2901    
2902  #endif /* !JS_TRACER */  #endif /* !JS_TRACER */
# Line 2508  Line 2911 
2911      JSString *str;      JSString *str;
2912    
2913      argv = vp + 2;      argv = vp + 2;
2914      JS_ASSERT(argc < ARRAY_INIT_LIMIT);      JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
2915      if (argc == 1 &&      if (argc == 1 &&
2916          (code = js_ValueToUint16(cx, &argv[0])) < UNIT_STRING_LIMIT) {          (code = js_ValueToUint16(cx, &argv[0])) < UNIT_STRING_LIMIT) {
2917          str = js_GetUnitStringForChar(cx, code);          str = JSString::unitString(code);
2918          if (!str)          if (!str)
2919              return JS_FALSE;              return JS_FALSE;
2920          *vp = STRING_TO_JSVAL(str);          *vp = STRING_TO_JSVAL(str);
2921          return JS_TRUE;          return JS_TRUE;
2922      }      }
2923      chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar));      chars = (jschar *) cx->malloc((argc + 1) * sizeof(jschar));
2924      if (!chars)      if (!chars)
2925          return JS_FALSE;          return JS_FALSE;
2926      for (i = 0; i < argc; i++) {      for (i = 0; i < argc; i++) {
2927          code = js_ValueToUint16(cx, &argv[i]);          code = js_ValueToUint16(cx, &argv[i]);
2928          if (JSVAL_IS_NULL(argv[i])) {          if (JSVAL_IS_NULL(argv[i])) {
2929              JS_free(cx, chars);              cx->free(chars);
2930              return JS_FALSE;              return JS_FALSE;
2931          }          }
2932          chars[i] = (jschar)code;          chars[i] = (jschar)code;
# Line 2531  Line 2934 
2934      chars[i] = 0;      chars[i] = 0;
2935      str = js_NewString(cx, chars, argc);      str = js_NewString(cx, chars, argc);
2936      if (!str) {      if (!str) {
2937          JS_free(cx, chars);          cx->free(chars);
2938          return JS_FALSE;          return JS_FALSE;
2939      }      }
2940      *vp = STRING_TO_JSVAL(str);      *vp = STRING_TO_JSVAL(str);
# Line 2545  Line 2948 
2948      JS_ASSERT(JS_ON_TRACE(cx));      JS_ASSERT(JS_ON_TRACE(cx));
2949      jschar c = (jschar)i;      jschar c = (jschar)i;
2950      if (c < UNIT_STRING_LIMIT)      if (c < UNIT_STRING_LIMIT)
2951          return js_GetUnitStringForChar(cx, c);          return JSString::unitString(c);
2952      return js_NewStringCopyN(cx, &c, 1);      return js_NewStringCopyN(cx, &c, 1);
2953  }  }
2954  #endif  #endif
# Line 2554  Line 2957 
2957      (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, 1)))      (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, 1)))
2958    
2959  static JSFunctionSpec string_static_methods[] = {  static JSFunctionSpec string_static_methods[] = {
2960      JS_TN("fromCharCode", str_fromCharCode, 1, 0, str_fromCharCode_trcinfo),      JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo),
2961      JS_FS_END      JS_FS_END
2962  };  };
2963    
# Line 2597  Line 3000 
3000      return JS_TRUE;      return JS_TRUE;
3001  }  }
3002    
 #define UNIT_STRING_SPACE(sp)    ((jschar *) ((sp) + UNIT_STRING_LIMIT))  
 #define UNIT_STRING_SPACE_RT(rt) UNIT_STRING_SPACE((rt)->unitStrings)  
   
 #define IN_UNIT_STRING_SPACE(sp,cp)                                           \  
     ((size_t)((cp) - UNIT_STRING_SPACE(sp)) < 2 * UNIT_STRING_LIMIT)  
 #define IN_UNIT_STRING_SPACE_RT(rt,cp)                                        \  
     IN_UNIT_STRING_SPACE((rt)->unitStrings, cp)  
   
 JSString *  
 js_GetUnitStringForChar(JSContext *cx, jschar c)  
 {  
     jschar *cp, i;  
     JSRuntime *rt;  
     JSString **sp;  
   
     JS_ASSERT(c < UNIT_STRING_LIMIT);  
     rt = cx->runtime;  
     if (!rt->unitStrings) {  
         sp = (JSString **) calloc(UNIT_STRING_LIMIT * sizeof(JSString *) +  
                                   UNIT_STRING_LIMIT * 2 * sizeof(jschar),  
                                   1);  
         if (!sp) {  
             JS_ReportOutOfMemory(cx);  
             return NULL;  
         }  
         cp = UNIT_STRING_SPACE(sp);  
         for (i = 0; i < UNIT_STRING_LIMIT; i++) {  
             *cp = i;  
             cp += 2;  
         }  
         JS_LOCK_GC(rt);  
         if (!rt->unitStrings) {  
             rt->unitStrings = sp;  
             JS_UNLOCK_GC(rt);  
         } else {  
             JS_UNLOCK_GC(rt);  
             free(sp);  
         }  
     }  
     if (!rt->unitStrings[c]) {  
         JSString *str;  
   
         cp = UNIT_STRING_SPACE_RT(rt);  
         str = js_NewString(cx, cp + 2 * c, 1);  
         if (!str)  
             return NULL;  
         JS_LOCK_GC(rt);  
         if (!rt->unitStrings[c])  
             rt->unitStrings[c] = str;  
 #ifdef DEBUG  
         else  
             JSFLATSTR_INIT(str, NULL, 0);  /* avoid later assertion (bug 479381) */  
 #endif  
         JS_UNLOCK_GC(rt);  
     }  
     return rt->unitStrings[c];  
 }  
   
 JSString *  
 js_GetUnitString(JSContext *cx, JSString *str, size_t index)  
 {  
     jschar c;  
   
     JS_ASSERT(index < JSSTRING_LENGTH(str));  
     c = JSSTRING_CHARS(str)[index];  
     if (c >= UNIT_STRING_LIMIT)  
         return js_NewDependentString(cx, str, index, 1);  
     return js_GetUnitStringForChar(cx, c);  
 }  
   
 void  
 js_FinishUnitStrings(JSRuntime *rt)  
 {  
     free(rt->unitStrings);  
     rt->unitStrings = NULL;  
 }  
   
3003  void  void
3004  js_FinishRuntimeStringState(JSContext *cx)  js_FinishRuntimeStringState(JSContext *cx)
3005  {  {
# Line 2709  Line 3035 
3035                           NULL, string_static_methods);                           NULL, string_static_methods);
3036      if (!proto)      if (!proto)
3037          return NULL;          return NULL;
3038      proto->fslots[JSSLOT_PRIVATE] = STRING_TO_JSVAL(cx->runtime->emptyString);      proto->fslots[JSSLOT_PRIMITIVE_THIS] = STRING_TO_JSVAL(cx->runtime->emptyString);
3039      if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),      if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
3040                                   JSVAL_VOID, NULL, NULL,                                   JSVAL_VOID, NULL, NULL,
3041                                   JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,                                   JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
# Line 2725  Line 3051 
3051  {  {
3052      JSString *str;      JSString *str;
3053    
3054      if (length > JSSTRING_LENGTH_MASK) {      if (length > JSString::MAX_LENGTH) {
3055          if (JS_ON_TRACE(cx)) {          if (JS_ON_TRACE(cx)) {
3056              /*              /*
3057               * If we can't leave the trace, signal OOM condition, otherwise               * If we can't leave the trace, signal OOM condition, otherwise
3058               * exit from trace and proceed with GC.               * exit from trace before throwing.
3059               */               */
3060              if (!js_CanLeaveTrace(cx))              if (!js_CanLeaveTrace(cx))
3061                  return NULL;                  return NULL;
# Line 2740  Line 3066 
3066          return NULL;          return NULL;
3067      }      }
3068    
3069      str = (JSString *) js_NewGCThing(cx, GCX_STRING, sizeof(JSString));      str = js_NewGCString(cx, GCX_STRING);
3070      if (!str)      if (!str)
3071          return NULL;          return NULL;
3072      JSFLATSTR_INIT(str, chars, length);      str->initFlat(chars, length);
3073  #ifdef DEBUG  #ifdef DEBUG
3074    {    {
3075      JSRuntime *rt = cx->runtime;      JSRuntime *rt = cx->runtime;
# Line 2757  Line 3083 
3083      return str;      return str;
3084  }  }
3085    
3086    static const size_t sMinWasteSize = 16;
3087    
3088    JSString *
3089    js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb)
3090    {
3091        if (cb.empty())
3092            return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
3093    
3094        size_t length = cb.length();
3095        if (!cb.append('\0'))
3096            return NULL;
3097    
3098        size_t capacity = cb.capacity();
3099    
3100        jschar *buf = cb.extractRawBuffer();
3101        if (!buf)
3102            return NULL;
3103    
3104        /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
3105        JS_ASSERT(capacity >= length);
3106        if (capacity > sMinWasteSize && capacity - length > (length >> 2)) {
3107            size_t bytes = sizeof(jschar) * (length + 1);
3108            jschar *tmp = (jschar *)cx->realloc(buf, bytes);
3109            if (!tmp) {
3110                cx->free(buf);
3111                return NULL;
3112            }
3113            buf = tmp;
3114        }
3115    
3116        JSString *str = js_NewString(cx, buf, length);
3117        if (!str)
3118            cx->free(buf);
3119        return str;
3120    }
3121    
3122  JSString *  JSString *
3123  js_NewDependentString(JSContext *cx, JSString *base, size_t start,  js_NewDependentString(JSContext *cx, JSString *base, size_t start,
3124                        size_t length)                        size_t length)
# Line 2766  Line 3128 
3128      if (length == 0)      if (length == 0)
3129          return cx->runtime->emptyString;          return cx->runtime->emptyString;
3130    
3131      if (start == 0 && length == JSSTRING_LENGTH(base))      if (start == 0 && length == base->length())
3132          return base;          return base;
3133    
3134      if (start > JSSTRDEP_START_MASK ||      if (start > JSString::MAX_DEPENDENT_START ||
3135          (start != 0 && length > JSSTRDEP_LENGTH_MASK)) {          (start != 0 && length > JSString::MAX_DEPENDENT_LENGTH)) {
3136          return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length);          return js_NewStringCopyN(cx, base->chars() + start, length);
3137      }      }
3138    
3139      ds = (JSString *)js_NewGCThing(cx, GCX_STRING, sizeof(JSString));      ds = js_NewGCString(cx, GCX_STRING);
3140      if (!ds)      if (!ds)
3141          return NULL;          return NULL;
3142      if (start == 0)      if (start == 0)
3143          JSPREFIX_INIT(ds, base, length);          ds->initPrefix(base, length);
3144      else      else
3145          JSSTRDEP_INIT(ds, base, start, length);          ds->initDependent(base, start, length);
3146  #ifdef DEBUG  #ifdef DEBUG
3147    {    {
3148      JSRuntime *rt = cx->runtime;      JSRuntime *rt = cx->runtime;
# Line 2826  Line 3188 
3188      jschar *news;      jschar *news;
3189      JSString *str;      JSString *str;
3190    
3191      news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));      news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
3192      if (!news)      if (!news)
3193          return NULL;          return NULL;
3194      js_strncpy(news, s, n);      js_strncpy(news, s, n);
3195      news[n] = 0;      news[n] = 0;
3196      str = js_NewString(cx, news, n);      str = js_NewString(cx, news, n);
3197      if (!str)      if (!str)
3198          JS_free(cx, news);          cx->free(news);
3199      return str;      return str;
3200  }  }
3201    
# Line 2846  Line 3208 
3208    
3209      n = js_strlen(s);      n = js_strlen(s);
3210      m = (n + 1) * sizeof(jschar);      m = (n + 1) * sizeof(jschar);
3211      news = (jschar *) JS_malloc(cx, m);      news = (jschar *) cx->malloc(m);
3212      if (!news)      if (!news)
3213          return NULL;          return NULL;
3214      memcpy(news, s, m);      memcpy(news, s, m);
3215      str = js_NewString(cx, news, n);      str = js_NewString(cx, news, n);
3216      if (!str)      if (!str)
3217          JS_free(cx, news);          cx->free(news);
3218      return str;      return str;
3219  }  }
3220    
# Line 2868  Line 3230 
3230      he = *hep;      he = *hep;
3231      if (he) {      if (he) {
3232  #ifdef DEBUG  #ifdef DEBUG
3233          rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str);          rt->deflatedStringCacheBytes -= str->length();
3234  #endif  #endif
3235          free(he->value);          js_free(he->value);
3236          JS_HashTableRawRemove(rt->deflatedStringCache, hep, he);          JS_HashTableRawRemove(rt->deflatedStringCache, hep, he);
3237      }      }
3238      JS_RELEASE_LOCK(rt->deflatedStringCacheLock);      JS_RELEASE_LOCK(rt->deflatedStringCacheLock);
3239  }  }
3240    
 static JSStringFinalizeOp str_finalizers[GCX_NTYPES - GCX_EXTERNAL_STRING] = {  
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL  
 };  
   
 intN  
 js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,  
                                  JSStringFinalizeOp newop)  
 {  
     uintN i;  
   
     for (i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) {  
         if (str_finalizers[i] == oldop) {  
             str_finalizers[i] = newop;  
             return (intN) i;  
         }  
     }  
     return -1;  
 }  
   
 /*  
  * cx is NULL when we are called from js_FinishAtomState to force the  
  * finalization of the permanently interned strings.  
  */  
 void  
 js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)  
 {  
     jschar *chars;  
     JSBool valid;  
     JSStringFinalizeOp finalizer;  
   
     JS_RUNTIME_UNMETER(rt, liveStrings);  
     if (JSSTRING_IS_DEPENDENT(str)) {  
         /* A dependent string can not be external and must be valid. */  
         JS_ASSERT(type < 0);  
         JS_ASSERT(JSSTRDEP_BASE(str));  
         JS_RUNTIME_UNMETER(rt, liveDependentStrings);  
         valid = JS_TRUE;  
     } else {  
         /* A stillborn string has null chars, so is not valid. */  
         chars = JSFLATSTR_CHARS(str);  
         valid = (chars != NULL);  
         if (valid) {  
             if (IN_UNIT_STRING_SPACE_RT(rt, chars)) {  
                 JS_ASSERT(rt->unitStrings[*chars] == str);  
                 JS_ASSERT(type < 0);  
                 rt->unitStrings[*chars] = NULL;  
             } else if (type < 0) {  
                 free(chars);  
             } else {  
                 JS_ASSERT((uintN) type < JS_ARRAY_LENGTH(str_finalizers));  
                 finalizer = str_finalizers[type];  
                 if (finalizer) {  
                     /*  
                      * Assume that the finalizer for the permanently interned  
                      * string knows how to deal with null context.  
                      */  
                     finalizer(cx, str);  
                 }  
             }  
         }  
     }  
     if (valid && JSSTRING_IS_DEFLATED(str))  
         js_PurgeDeflatedStringCache(rt, str);  
 }  
   
3241  JS_FRIEND_API(const char *)  JS_FRIEND_API(const char *)
3242  js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun)  js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun)
3243  {  {
# Line 2958  Line 3255 
3255  JS_FRIEND_API(JSString *)  JS_FRIEND_API(JSString *)
3256  js_ValueToString(JSContext *cx, jsval v)  js_ValueToString(JSContext *cx, jsval v)
3257  {  {
     JSObject *obj;  
3258      JSString *str;      JSString *str;
3259    
3260      if (JSVAL_IS_OBJECT(v)) {      if (!JSVAL_IS_PRIMITIVE(v) && !JSVAL_TO_OBJECT(v)->defaultValue(cx, JSTYPE_STRING, &v))
3261          obj = JSVAL_TO_OBJECT(v);          return NULL;
3262          if (!obj)  
             return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);  
         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))  
             return NULL;  
     }  
3263      if (JSVAL_IS_STRING(v)) {      if (JSVAL_IS_STRING(v)) {
3264          str = JSVAL_TO_STRING(v);          str = JSVAL_TO_STRING(v);
3265      } else if (JSVAL_IS_INT(v)) {      } else if (JSVAL_IS_INT(v)) {
# Line 2976  Line 3268 
3268          str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v));          str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v));
3269      } else if (JSVAL_IS_BOOLEAN(v)) {      } else if (JSVAL_IS_BOOLEAN(v)) {
3270          str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v));          str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v));
3271        } else if (JSVAL_IS_NULL(v)) {
3272            str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3273      } else {      } else {
3274          str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);          str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
3275      }      }
3276      return str;      return str;
3277  }  }
3278    
3279    static inline JSBool
3280    pushAtom(JSAtom *atom, JSCharBuffer &cb)
3281    {
3282        JSString *str = ATOM_TO_STRING(atom);
3283        const jschar *chars;
3284        size_t length;
3285        str->getCharsAndLength(chars, length);
3286        return cb.append(chars, length);
3287    }
3288    
3289    /* This function implements E-262-3 section 9.8, toString. */
3290    JS_FRIEND_API(JSBool)
3291    js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb)
3292    {
3293        if (!JSVAL_IS_PRIMITIVE(v) && !JSVAL_TO_OBJECT(v)->defaultValue(cx, JSTYPE_STRING, &v))
3294            return JS_FALSE;
3295    
3296        if (JSVAL_IS_STRING(v)) {
3297            JSString *str = JSVAL_TO_STRING(v);
3298            const jschar *chars;
3299            size_t length;
3300            str->getCharsAndLength(chars, length);
3301            return cb.append(chars, length);
3302        }
3303        if (JSVAL_IS_NUMBER(v))
3304            return js_NumberValueToCharBuffer(cx, v, cb);
3305        if (JSVAL_IS_BOOLEAN(v))
3306            return js_BooleanToCharBuffer(cx, JSVAL_TO_BOOLEAN(v), cb);
3307        if (JSVAL_IS_NULL(v))
3308            return pushAtom(cx->runtime->atomState.nullAtom, cb);
3309        JS_ASSERT(JSVAL_IS_VOID(v));
3310        return pushAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], cb);
3311    }
3312    
3313  JS_FRIEND_API(JSString *)  JS_FRIEND_API(JSString *)
3314  js_ValueToSource(JSContext *cx, jsval v)  js_ValueToSource(JSContext *cx, jsval v)
3315  {  {
     JSTempValueRooter tvr;  
     JSString *str;  
   
3316      if (JSVAL_IS_VOID(v))      if (JSVAL_IS_VOID(v))
3317          return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);          return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);
3318      if (JSVAL_IS_STRING(v))      if (JSVAL_IS_STRING(v))
# Line 3003  Line 3328 
3328          return js_ValueToString(cx, v);          return js_ValueToString(cx, v);
3329      }      }
3330    
3331      JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);      JSAtom *atom = cx->runtime->atomState.toSourceAtom;
3332      if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v),      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
3333                        cx->runtime->atomState.toSourceAtom,      if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), atom, 0, NULL, tvr.addr()))
3334                        0, NULL, &tvr.u.value)) {          return NULL;
3335          str = NULL;      return js_ValueToString(cx, tvr.value());
     } else {  
         str = js_ValueToString(cx, tvr.u.value);  
     }  
     JS_POP_TEMP_ROOT(cx, &tvr);  
     return str;  
3336  }  }
3337    
3338  /*  /*
# Line 3025  Line 3345 
3345      size_t n;      size_t n;
3346      uint32 h;      uint32 h;
3347    
3348      JSSTRING_CHARS_AND_LENGTH(str, s, n);      str->getCharsAndLength(s, n);
3349      for (h = 0; n; s++, n--)      for (h = 0; n; s++, n--)
3350          h = JS_ROTATE_LEFT32(h, 4) ^ *s;          h = JS_ROTATE_LEFT32(h, 4) ^ *s;
3351      return h;      return h;
# Line 3047  Line 3367 
3367      if (str1 == str2)      if (str1 == str2)
3368          return JS_TRUE;          return JS_TRUE;
3369    
3370      n = JSSTRING_LENGTH(str1);      n = str1->length();
3371      if (n != JSSTRING_LENGTH(str2))      if (n != str2->length())
3372          return JS_FALSE;          return JS_FALSE;
3373    
3374      if (n == 0)      if (n == 0)
3375          return JS_TRUE;          return JS_TRUE;
3376    
3377      s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2);      s1 = str1->chars(), s2 = str2->chars();
3378      do {      do {
3379          if (*s1 != *s2)          if (*s1 != *s2)
3380              return JS_FALSE;              return JS_FALSE;
# Line 3063  Line 3383 
3383    
3384      return JS_TRUE;      return JS_TRUE;
3385  }  }
3386    JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, 1)
3387    
3388  int32 JS_FASTCALL  int32 JS_FASTCALL
3389  js_CompareStrings(JSString *str1, JSString *str2)  js_CompareStrings(JSString *str1, JSString *str2)
# Line 3078  Line 3399 
3399      if (str1 == str2)      if (str1 == str2)
3400          return 0;          return 0;
3401    
3402      JSSTRING_CHARS_AND_LENGTH(str1, s1, l1);      str1->getCharsAndLength(s1, l1);
3403      JSSTRING_CHARS_AND_LENGTH(str2, s2, l2);      str2->getCharsAndLength(s2, l2);
3404      n = JS_MIN(l1, l2);      n = JS_MIN(l1, l2);
3405      for (i = 0; i < n; i++) {      for (i = 0; i < n; i++) {
3406          cmp = s1[i] - s2[i];          cmp = s1[i] - s2[i];
# Line 3088  Line 3409 
3409      }      }
3410      return (intN)(l1 - l2);      return (intN)(l1 - l2);
3411  }  }
3412    JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, 1)
3413    
3414  size_t  size_t
3415  js_strlen(const jschar *s)  js_strlen(const jschar *s)
# Line 3121  Line 3443 
3443      return NULL;      return NULL;
3444  }  }
3445    
 const jschar *  
 js_SkipWhiteSpace(const jschar *s, const jschar *end)  
 {  
     JS_ASSERT(s <= end);  
     while (s != end && JS_ISSPACE(*s))  
         s++;  
     return s;  
 }  
   
3446  jschar *  jschar *
3447  js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp)  js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp)
3448  {  {
# Line 3143  Line 3456 
3456      if (js_CStringsAreUTF8) {      if (js_CStringsAreUTF8) {
3457          if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars))          if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars))
3458              goto bad;              goto bad;
3459          chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof (jschar));          chars = (jschar *) cx->malloc((nchars + 1) * sizeof (jschar));
3460          if (!chars)          if (!chars)
3461              goto bad;              goto bad;
3462  #ifdef DEBUG  #ifdef DEBUG
# Line 3153  Line 3466 
3466          JS_ASSERT(ok);          JS_ASSERT(ok);
3467      } else {      } else {
3468          nchars = nbytes;          nchars = nbytes;
3469          chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));          chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
3470          if (!chars)          if (!chars)
3471              goto bad;              goto bad;
3472          for (i = 0; i < nchars; i++)          for (i = 0; i < nchars; i++)
# Line 3188  Line 3501 
3501          nbytes = js_GetDeflatedStringLength(cx, chars, nchars);          nbytes = js_GetDeflatedStringLength(cx, chars, nchars);
3502          if (nbytes == (size_t) -1)          if (nbytes == (size_t) -1)
3503              return NULL;              return NULL;
3504          bytes = (char *) (cx ? JS_malloc(cx, nbytes + 1) : malloc(nbytes + 1));          bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3505          if (!bytes)          if (!bytes)
3506              return NULL;              return NULL;
3507  #ifdef DEBUG  #ifdef DEBUG
# Line 3198  Line 3511 
3511          JS_ASSERT(ok);          JS_ASSERT(ok);
3512      } else {      } else {
3513          nbytes = nchars;          nbytes = nchars;
3514          bytes = (char *) (cx ? JS_malloc(cx, nbytes + 1) : malloc(nbytes + 1));          bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3515          if (!bytes)          if (!bytes)
3516              return NULL;              return NULL;
3517          for (i = 0; i < nbytes; i++)          for (i = 0; i < nbytes; i++)
# Line 3371  Line 3684 
3684                  n++;                  n++;
3685              if (n > srclen)              if (n > srclen)
3686                  goto bufferTooSmall;                  goto bufferTooSmall;
3687              if (n == 1 || n > 6)              if (n == 1 || n > 4)
3688                  goto badCharacter;                  goto badCharacter;
3689              for (j = 1; j < n; j++) {              for (j = 1; j < n; j++) {
3690                  if ((src[j] & 0xC0) != 0x80)                  if ((src[j] & 0xC0) != 0x80)
# Line 3453  Line 3766 
3766      JS_ASSERT(*hep == NULL);      JS_ASSERT(*hep == NULL);
3767      ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL;      ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL;
3768      if (ok) {      if (ok) {
3769          JSSTRING_SET_DEFLATED(str);          str->setDeflated();
3770  #ifdef DEBUG  #ifdef DEBUG
3771          rt->deflatedStringCacheBytes += length;          rt->deflatedStringCacheBytes += length;
3772  #endif  #endif
# Line 3472  Line 3785 
3785      JSHashNumber hash;      JSHashNumber hash;
3786      JSHashEntry *he, **hep;      JSHashEntry *he, **hep;
3787    
3788        if (JSString::isUnitString(str)) {
3789    #ifdef IS_LITTLE_ENDIAN
3790            /* Unit string data is {c, 0, 0, 0} so we can just cast. */
3791            return (char *)str->chars();
3792    #else
3793            /* Unit string data is {0, c, 0, 0} so we can point into the middle. */
3794            return (char *)str->chars() + 1;
3795    #endif            
3796        }
3797    
3798        if (JSString::isIntString(str)) {
3799            /*
3800             * We must burn some space on deflated int strings to preserve static
3801             * allocation (which is to say, JSRuntime independence).
3802             */
3803            return JSString::deflatedIntStringTable[str - JSString::intStringTable];
3804        }
3805    
3806      if (cx) {      if (cx) {
3807          rt = cx->runtime;          rt = cx->runtime;
3808      } else {      } else {
# Line 3485  Line 3816 
3816           * Called from last GC (see js_DestroyContext), after runtime string           * Called from last GC (see js_DestroyContext), after runtime string
3817           * state has been finalized.  We have no choice but to leak here.           * state has been finalized.  We have no choice but to leak here.
3818           */           */
3819          return js_DeflateString(NULL, JSSTRING_CHARS(str),          return js_DeflateString(NULL, str->chars(), str->length());
                                       JSSTRING_LENGTH(str));  
3820      }      }
3821  #endif  #endif
3822    
# Line 3501  Line 3831 
3831    
3832          /* Try to catch failure to JS_ShutDown between runtime epochs. */          /* Try to catch failure to JS_ShutDown between runtime epochs. */
3833          if (!js_CStringsAreUTF8) {          if (!js_CStringsAreUTF8) {
3834              JS_ASSERT_IF(*bytes != (char) JSSTRING_CHARS(str)[0],              JS_ASSERT_IF(*bytes != (char) str->chars()[0],
3835                           *bytes == '\0' && JSSTRING_LENGTH(str) == 0);                           *bytes == '\0' && str->empty());
3836          }          }
3837      } else {      } else {
3838          bytes = js_DeflateString(cx, JSSTRING_CHARS(str),          bytes = js_DeflateString(cx, str->chars(), str->length());
                                  JSSTRING_LENGTH(str));  
3839          if (bytes) {          if (bytes) {
3840              if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) {              if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) {
3841  #ifdef DEBUG  #ifdef DEBUG
3842                  rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str);                  rt->deflatedStringCacheBytes += str->length();
3843  #endif  #endif
3844                  JSSTRING_SET_DEFLATED(str);                  str->setDeflated();
3845              } else {              } else {
3846                  if (cx)                  if (cx)
3847                      JS_free(cx, bytes);                      cx->free(bytes);
3848                  else                  else
3849                      free(bytes);                      js_free(bytes);
3850                  bytes = NULL;                  bytes = NULL;
3851              }              }
3852          }          }
# Line 4825  Line 5154 
5154       'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',       'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
5155       '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};       '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
5156    
5157  #define URI_CHUNK 64U  /*
5158     * This table allows efficient testing for the regular expression \w which is
5159  /* Concatenate jschars onto the buffer */   * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z].
5160  static JSBool   */
5161  AddCharsToURI(JSContext *cx, JSCharBuffer *buf,  const bool js_alnum[] = {
5162                const jschar *chars, size_t length)  /*       0      1      2      3      4      5      5      7      8      9      */
5163  {  /*  0 */ false, false, false, false, false, false, false, false, false, false,
5164      size_t total;  /*  1 */ false, false, false, false, false, false, false, false, false, false,
5165      jschar *newchars;  /*  2 */ false, false, false, false, false, false, false, false, false, false,
5166    /*  3 */ false, false, false, false, false, false, false, false, false, false,
5167    /*  4 */ false, false, false, false, false, false, false, false, true,  true,
5168    /*  5 */ true,  true,  true,  true,  true,  true,  true,  true,  false, false,
5169    /*  6 */ false, false, false, false, false, true,  true,  true,  true,  true,
5170    /*  7 */ true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
5171    /*  8 */ true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
5172    /*  9 */ true,  false, false, false, false, true,  false, true,  true,  true,
5173    /* 10 */ true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
5174    /* 11 */ true,  true,  true,  true,  true,  true,  true,  true,  true,  true,
5175    /* 12 */ true,  true,  true,  false, false, false, false, false
5176    };
5177    
5178      total = buf->length + length + 1;  #define URI_CHUNK 64U
     if (!buf->chars ||  
         JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(buf->length + 1, URI_CHUNK)) {  
         total = JS_ROUNDUP(total, URI_CHUNK);  
         newchars = (jschar *) JS_realloc(cx, buf->chars,  
                                          total * sizeof(jschar));  
         if (!newchars)  
             return JS_FALSE;  
         buf->chars = newchars;  
     }  
     js_strncpy(buf->chars + buf->length, chars, length);  
     buf->length += length;  
     buf->chars[buf->length] = 0;  
     return JS_TRUE;  
 }  
5179    
5180  static JSBool  static inline bool
5181  TransferBufferToString(JSContext *cx, JSCharBuffer *cb, jsval *rval)  TransferBufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval)
5182  {  {
5183      jschar *chars;      JSString *str = js_NewStringFromCharBuffer(cx, cb);
     size_t n;  
     JSString *str;  
   
     /*  
      * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we  
      * don't worry about that case here.  
      */  
     n = cb->length;  
     chars = (jschar *) JS_realloc(cx, cb->chars, (n + 1) * sizeof(jschar));  
     if (!chars)  
         chars = cb->chars;  
     str = js_NewString(cx, chars, n);  
5184      if (!str)      if (!str)
5185          return JS_FALSE;          return false;
   
     /* Successful allocation transfer ownership of cb->chars to the string. */  
 #ifdef DEBUG  
     memset(cb, JS_FREE_PATTERN, sizeof *cb);  
 #endif  
   
5186      *rval = STRING_TO_JSVAL(str);      *rval = STRING_TO_JSVAL(str);
5187      return JS_TRUE;      return true;;
5188  }  }
5189    
5190  /*  /*
# Line 4891  Line 5199 
5199         const jschar *unescapedSet2, jsval *rval)         const jschar *unescapedSet2, jsval *rval)
5200  {  {
5201      size_t length, j, k, L;      size_t length, j, k, L;
5202      JSCharBuffer cb;      JSCharBuffer cb(cx);
5203      jschar *chars, c, c2;      const jschar *chars;
5204        jschar c, c2;
5205      uint32 v;      uint32 v;
5206      uint8 utf8buf[6];      uint8 utf8buf[4];
5207      jschar hexBuf[4];      jschar hexBuf[4];
5208      static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */      static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
5209    
5210      JSSTRING_CHARS_AND_LENGTH(str, chars, length);      str->getCharsAndLength(chars, length);
5211      if (length == 0) {      if (length == 0) {
5212          *rval = STRING_TO_JSVAL(cx->runtime->emptyString);          *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
5213          return JS_TRUE;          return JS_TRUE;
5214      }      }
5215    
     cb.length = 0;  
     cb.chars = NULL;  
   
5216      /* From this point the control must goto bad on failures. */      /* From this point the control must goto bad on failures. */
5217      hexBuf[0] = '%';      hexBuf[0] = '%';
5218      hexBuf[3] = 0;      hexBuf[3] = 0;
# Line 4914  Line 5220 
5220          c = chars[k];          c = chars[k];
5221          if (js_strchr(unescapedSet, c) ||          if (js_strchr(unescapedSet, c) ||
5222              (unescapedSet2 && js_strchr(unescapedSet2, c))) {              (unescapedSet2 && js_strchr(unescapedSet2, c))) {
5223              if (!AddCharsToURI(cx, &cb, &c, 1))              if (!cb.append(c))
5224                  goto bad;                  return JS_FALSE;
5225          } else {          } else {
5226              if ((c >= 0xDC00) && (c <= 0xDFFF)) {              if ((c >= 0xDC00) && (c <= 0xDFFF)) {
5227                  JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                  JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5228                                   JSMSG_BAD_URI, NULL);                                   JSMSG_BAD_URI, NULL);
5229                  goto bad;                  return JS_FALSE;
5230              }              }
5231              if (c < 0xD800 || c > 0xDBFF) {              if (c < 0xD800 || c > 0xDBFF) {
5232                  v = c;                  v = c;
# Line 4929  Line 5235 
5235                  if (k == length) {                  if (k == length) {
5236                      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5237                                       JSMSG_BAD_URI, NULL);                                       JSMSG_BAD_URI, NULL);
5238                      goto bad;                      return JS_FALSE;
5239                  }                  }
5240                  c2 = chars[k];                  c2 = chars[k];
5241                  if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {                  if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
5242                      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5243                                       JSMSG_BAD_URI, NULL);                                       JSMSG_BAD_URI, NULL);
5244                      goto bad;                      return JS_FALSE;
5245                  }                  }
5246                  v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;                  v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
5247              }              }
# Line 4943  Line 5249 
5249              for (j = 0; j < L; j++) {              for (j = 0; j < L; j++) {
5250                  hexBuf[1] = HexDigits[utf8buf[j] >> 4];                  hexBuf[1] = HexDigits[utf8buf[j] >> 4];
5251                  hexBuf[2] = HexDigits[utf8buf[j] & 0xf];                  hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
5252                  if (!AddCharsToURI(cx, &cb, hexBuf, 3))                  if (!cb.append(hexBuf, 3))
5253                      goto bad;                      return JS_FALSE;
5254              }              }
5255          }          }
5256      }      }
5257    
5258      if (!TransferBufferToString(cx, &cb, rval))      return TransferBufferToString(cx, cb, rval);
         goto bad;  
   
     return JS_TRUE;  
   
   bad:  
     JS_free(cx, cb.chars);  
     return JS_FALSE;  
5259  }  }
5260    
5261  static JSBool  static JSBool
5262  Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)  Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
5263  {  {
5264      size_t length, start, k;      size_t length, start, k;
5265      JSCharBuffer cb;      JSCharBuffer cb(cx);
5266      jschar *chars, c, H;      const jschar *chars;
5267        jschar c, H;
5268      uint32 v;      uint32 v;
5269      jsuint B;      jsuint B;
5270      uint8 octets[6];      uint8 octets[4];
5271      intN j, n;      intN j, n;
5272    
5273      JSSTRING_CHARS_AND_LENGTH(str, chars, length);      str->getCharsAndLength(chars, length);
5274      if (length == 0) {      if (length == 0) {
5275          *rval = STRING_TO_JSVAL(cx->runtime->emptyString);          *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
5276          return JS_TRUE;          return JS_TRUE;
5277      }      }
5278    
     cb.length = 0;  
     cb.chars = NULL;  
   
5279      /* From this point the control must goto bad on failures. */      /* From this point the control must goto bad on failures. */
5280      for (k = 0; k < length; k++) {      for (k = 0; k < length; k++) {
5281          c = chars[k];          c = chars[k];
# Line 4996  Line 5293 
5293                  n = 1;                  n = 1;
5294                  while (B & (0x80 >> n))                  while (B & (0x80 >> n))
5295                      n++;                      n++;
5296                  if (n == 1 || n > 6)                  if (n == 1 || n > 4)
5297                      goto report_bad_uri;                      goto report_bad_uri;
5298                  octets[0] = (uint8)B;                  octets[0] = (uint8)B;
5299                  if (k + 3 * (n - 1) >= length)                  if (k + 3 * (n - 1) >= length)
# Line 5020  Line 5317 
5317                          goto report_bad_uri;                          goto report_bad_uri;
5318                      c = (jschar)((v & 0x3FF) + 0xDC00);                      c = (jschar)((v & 0x3FF) + 0xDC00);
5319                      H = (jschar)((v >> 10) + 0xD800);                      H = (jschar)((v >> 10) + 0xD800);
5320                      if (!AddCharsToURI(cx, &cb, &H, 1))                      if (!cb.append(H))
5321                          goto bad;                          return JS_FALSE;
5322                  } else {                  } else {
5323                      c = (jschar)v;                      c = (jschar)v;
5324                  }                  }
5325              }              }
5326              if (js_strchr(reservedSet, c)) {              if (js_strchr(reservedSet, c)) {
5327                  if (!AddCharsToURI(cx, &cb, &chars[start], (k - start + 1)))                  if (!cb.append(chars + start, k - start + 1))
5328                      goto bad;                      return JS_FALSE;
5329              } else {              } else {
5330                  if (!AddCharsToURI(cx, &cb, &c, 1))                  if (!cb.append(c))
5331                      goto bad;                      return JS_FALSE;
5332              }              }
5333          } else {          } else {
5334              if (!AddCharsToURI(cx, &cb, &c, 1))              if (!cb.append(c))
5335                  return JS_FALSE;                  return JS_FALSE;
5336          }          }
5337      }      }
5338    
5339      if (!TransferBufferToString(cx, &cb, rval))      return TransferBufferToString(cx, cb, rval);
         goto bad;  
   
     return JS_TRUE;  
5340    
5341    report_bad_uri:    report_bad_uri:
5342      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
5343      /* FALL THROUGH */      /* FALL THROUGH */
5344    
   bad:  
     JS_free(cx, cb.chars);  
5345      return JS_FALSE;      return JS_FALSE;
5346  }  }
5347    
# Line 5100  Line 5392 
5392    
5393  /*  /*
5394   * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at   * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
5395   * least 6 bytes long.  Return the number of UTF-8 bytes of data written.   * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
5396   */   */
5397  int  int
5398  js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)  js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)
5399  {  {
5400      int utf8Length = 1;      int utf8Length = 1;
5401    
5402      JS_ASSERT(ucs4Char <= 0x7FFFFFFF);      JS_ASSERT(ucs4Char <= 0x10FFFF);
5403      if (ucs4Char < 0x80) {      if (ucs4Char < 0x80) {
5404          *utf8Buffer = (uint8)ucs4Char;          *utf8Buffer = (uint8)ucs4Char;
5405      } else {      } else {
# Line 5140  Line 5432 
5432      uint32 minucs4Char;      uint32 minucs4Char;
5433      /* from Unicode 3.1, non-shortest form is illegal */      /* from Unicode 3.1, non-shortest form is illegal */
5434      static const uint32 minucs4Table[] = {      static const uint32 minucs4Table[] = {
5435          0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000          0x00000080, 0x00000800, 0x00010000
5436      };      };
5437    
5438      JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);      JS_ASSERT(utf8Length >= 1 && utf8Length <= 4);
5439      if (utf8Length == 1) {      if (utf8Length == 1) {
5440          ucs4Char = *utf8Buffer;          ucs4Char = *utf8Buffer;
5441          JS_ASSERT(!(ucs4Char & 0x80));          JS_ASSERT(!(ucs4Char & 0x80));
# Line 5156  Line 5448 
5448              JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);              JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
5449              ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);              ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
5450          }          }
5451          if (ucs4Char < minucs4Char ||          if (JS_UNLIKELY(ucs4Char < minucs4Char)) {
5452              ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {              ucs4Char = OVERLONG_UTF8;
5453            } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
5454              ucs4Char = 0xFFFD;              ucs4Char = 0xFFFD;
5455          }          }
5456      }      }
# Line 5170  Line 5463 
5463  js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,  js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,
5464                          JSString *str, uint32 quote)                          JSString *str, uint32 quote)
5465  {  {
5466      jschar *chars, *charsEnd;      const jschar *chars, *charsEnd;
5467      size_t n;      size_t n;
5468      const char *escape;      const char *escape;
5469      char c;      char c;
# Line 5184  Line 5477 
5477      JS_ASSERT_IF(!buffer, bufferSize == 0);      JS_ASSERT_IF(!buffer, bufferSize == 0);
5478      JS_ASSERT_IF(fp, !buffer);      JS_ASSERT_IF(fp, !buffer);
5479    
5480      JSSTRING_CHARS_AND_END(str, chars, charsEnd);      str->getCharsAndEnd(chars, charsEnd);
5481      n = 0;      n = 0;
5482      --bufferSize;      --bufferSize;
5483      state = FIRST_QUOTE;      state = FIRST_QUOTE;

Legend:
Removed from v.460  
changed lines
  Added in v.507

  ViewVC Help
Powered by ViewVC 1.1.24