Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      4  *  Copyright (C) 2009 Torch Mobile, Inc.
      5  *
      6  *  This library is free software; you can redistribute it and/or
      7  *  modify it under the terms of the GNU Lesser General Public
      8  *  License as published by the Free Software Foundation; either
      9  *  version 2 of the License, or (at your option) any later version.
     10  *
     11  *  This library is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  *  Lesser General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU Lesser General Public
     17  *  License along with this library; if not, write to the Free Software
     18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "StringPrototype.h"
     24 
     25 #include "CachedCall.h"
     26 #include "Error.h"
     27 #include "Executable.h"
     28 #include "JSGlobalObjectFunctions.h"
     29 #include "JSArray.h"
     30 #include "JSFunction.h"
     31 #include "JSStringBuilder.h"
     32 #include "ObjectPrototype.h"
     33 #include "Operations.h"
     34 #include "PropertyNameArray.h"
     35 #include "RegExpConstructor.h"
     36 #include "RegExpObject.h"
     37 #include <wtf/ASCIICType.h>
     38 #include <wtf/MathExtras.h>
     39 #include <wtf/unicode/Collator.h>
     40 
     41 using namespace WTF;
     42 
     43 namespace JSC {
     44 
     45 ASSERT_CLASS_FITS_IN_CELL(StringPrototype);
     46 
     47 static JSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&);
     48 static JSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*, JSObject*, JSValue, const ArgList&);
     49 static JSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*, JSObject*, JSValue, const ArgList&);
     50 static JSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*, JSObject*, JSValue, const ArgList&);
     51 static JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*, JSObject*, JSValue, const ArgList&);
     52 static JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*, JSObject*, JSValue, const ArgList&);
     53 static JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*, JSObject*, JSValue, const ArgList&);
     54 static JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*, JSObject*, JSValue, const ArgList&);
     55 static JSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*, JSObject*, JSValue, const ArgList&);
     56 static JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*, JSObject*, JSValue, const ArgList&);
     57 static JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*, JSObject*, JSValue, const ArgList&);
     58 static JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*, JSObject*, JSValue, const ArgList&);
     59 static JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*, JSObject*, JSValue, const ArgList&);
     60 static JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*, JSObject*, JSValue, const ArgList&);
     61 static JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*, JSObject*, JSValue, const ArgList&);
     62 static JSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*, JSObject*, JSValue, const ArgList&);
     63 
     64 static JSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*, JSObject*, JSValue, const ArgList&);
     65 static JSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*, JSObject*, JSValue, const ArgList&);
     66 static JSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*, JSObject*, JSValue, const ArgList&);
     67 static JSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*, JSObject*, JSValue, const ArgList&);
     68 static JSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*, JSObject*, JSValue, const ArgList&);
     69 static JSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*, JSObject*, JSValue, const ArgList&);
     70 static JSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*, JSObject*, JSValue, const ArgList&);
     71 static JSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*, JSObject*, JSValue, const ArgList&);
     72 static JSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*, JSObject*, JSValue, const ArgList&);
     73 static JSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*, JSObject*, JSValue, const ArgList&);
     74 static JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*, JSObject*, JSValue, const ArgList&);
     75 static JSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*, JSObject*, JSValue, const ArgList&);
     76 static JSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*, JSObject*, JSValue, const ArgList&);
     77 
     78 static JSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*, JSObject*, JSValue, const ArgList&);
     79 static JSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*, JSObject*, JSValue, const ArgList&);
     80 static JSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*, JSObject*, JSValue, const ArgList&);
     81 
     82 }
     83 
     84 #include "StringPrototype.lut.h"
     85 
     86 namespace JSC {
     87 
     88 const ClassInfo StringPrototype::info = { "String", &StringObject::info, 0, ExecState::stringTable };
     89 
     90 /* Source for StringPrototype.lut.h
     91 @begin stringTable 26
     92     toString              stringProtoFuncToString          DontEnum|Function       0
     93     valueOf               stringProtoFuncToString          DontEnum|Function       0
     94     charAt                stringProtoFuncCharAt            DontEnum|Function       1
     95     charCodeAt            stringProtoFuncCharCodeAt        DontEnum|Function       1
     96     concat                stringProtoFuncConcat            DontEnum|Function       1
     97     indexOf               stringProtoFuncIndexOf           DontEnum|Function       1
     98     lastIndexOf           stringProtoFuncLastIndexOf       DontEnum|Function       1
     99     match                 stringProtoFuncMatch             DontEnum|Function       1
    100     replace               stringProtoFuncReplace           DontEnum|Function       2
    101     search                stringProtoFuncSearch            DontEnum|Function       1
    102     slice                 stringProtoFuncSlice             DontEnum|Function       2
    103     split                 stringProtoFuncSplit             DontEnum|Function       2
    104     substr                stringProtoFuncSubstr            DontEnum|Function       2
    105     substring             stringProtoFuncSubstring         DontEnum|Function       2
    106     toLowerCase           stringProtoFuncToLowerCase       DontEnum|Function       0
    107     toUpperCase           stringProtoFuncToUpperCase       DontEnum|Function       0
    108     localeCompare         stringProtoFuncLocaleCompare     DontEnum|Function       1
    109 
    110     # toLocaleLowerCase and toLocaleUpperCase are currently identical to toLowerCase and toUpperCase
    111     toLocaleLowerCase     stringProtoFuncToLowerCase       DontEnum|Function       0
    112     toLocaleUpperCase     stringProtoFuncToUpperCase       DontEnum|Function       0
    113 
    114     big                   stringProtoFuncBig               DontEnum|Function       0
    115     small                 stringProtoFuncSmall             DontEnum|Function       0
    116     blink                 stringProtoFuncBlink             DontEnum|Function       0
    117     bold                  stringProtoFuncBold              DontEnum|Function       0
    118     fixed                 stringProtoFuncFixed             DontEnum|Function       0
    119     italics               stringProtoFuncItalics           DontEnum|Function       0
    120     strike                stringProtoFuncStrike            DontEnum|Function       0
    121     sub                   stringProtoFuncSub               DontEnum|Function       0
    122     sup                   stringProtoFuncSup               DontEnum|Function       0
    123     fontcolor             stringProtoFuncFontcolor         DontEnum|Function       1
    124     fontsize              stringProtoFuncFontsize          DontEnum|Function       1
    125     anchor                stringProtoFuncAnchor            DontEnum|Function       1
    126     link                  stringProtoFuncLink              DontEnum|Function       1
    127     trim                  stringProtoFuncTrim              DontEnum|Function       0
    128     trimLeft              stringProtoFuncTrimLeft          DontEnum|Function       0
    129     trimRight             stringProtoFuncTrimRight         DontEnum|Function       0
    130 @end
    131 */
    132 
    133 // ECMA 15.5.4
    134 StringPrototype::StringPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure)
    135     : StringObject(exec, structure)
    136 {
    137     // The constructor will be added later, after StringConstructor has been built
    138     putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum);
    139 }
    140 
    141 bool StringPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot)
    142 {
    143     return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), this, propertyName, slot);
    144 }
    145 
    146 bool StringPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    147 {
    148     return getStaticFunctionDescriptor<StringObject>(exec, ExecState::stringTable(exec), this, propertyName, descriptor);
    149 }
    150 
    151 // ------------------------------ Functions --------------------------
    152 
    153 static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, int i)
    154 {
    155     Vector<UChar> substitutedReplacement;
    156     int offset = 0;
    157     do {
    158         if (i + 1 == replacement.size())
    159             break;
    160 
    161         UChar ref = replacement[i + 1];
    162         if (ref == '$') {
    163             // "$$" -> "$"
    164             ++i;
    165             substitutedReplacement.append(replacement.data() + offset, i - offset);
    166             offset = i + 1;
    167             continue;
    168         }
    169 
    170         int backrefStart;
    171         int backrefLength;
    172         int advance = 0;
    173         if (ref == '&') {
    174             backrefStart = ovector[0];
    175             backrefLength = ovector[1] - backrefStart;
    176         } else if (ref == '`') {
    177             backrefStart = 0;
    178             backrefLength = ovector[0];
    179         } else if (ref == '\'') {
    180             backrefStart = ovector[1];
    181             backrefLength = source.size() - backrefStart;
    182         } else if (reg && ref >= '0' && ref <= '9') {
    183             // 1- and 2-digit back references are allowed
    184             unsigned backrefIndex = ref - '0';
    185             if (backrefIndex > reg->numSubpatterns())
    186                 continue;
    187             if (replacement.size() > i + 2) {
    188                 ref = replacement[i + 2];
    189                 if (ref >= '0' && ref <= '9') {
    190                     backrefIndex = 10 * backrefIndex + ref - '0';
    191                     if (backrefIndex > reg->numSubpatterns())
    192                         backrefIndex = backrefIndex / 10;   // Fall back to the 1-digit reference
    193                     else
    194                         advance = 1;
    195                 }
    196             }
    197             if (!backrefIndex)
    198                 continue;
    199             backrefStart = ovector[2 * backrefIndex];
    200             backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
    201         } else
    202             continue;
    203 
    204         if (i - offset)
    205             substitutedReplacement.append(replacement.data() + offset, i - offset);
    206         i += 1 + advance;
    207         offset = i + 1;
    208         substitutedReplacement.append(source.data() + backrefStart, backrefLength);
    209     } while ((i = replacement.find('$', i + 1)) != -1);
    210 
    211     if (replacement.size() - offset)
    212         substitutedReplacement.append(replacement.data() + offset, replacement.size() - offset);
    213 
    214     substitutedReplacement.shrinkToFit();
    215     return UString::adopt(substitutedReplacement);
    216 }
    217 
    218 static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg)
    219 {
    220     int i = replacement.find('$', 0);
    221     if (UNLIKELY(i != -1))
    222         return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
    223     return replacement;
    224 }
    225 
    226 static inline int localeCompare(const UString& a, const UString& b)
    227 {
    228     return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.data()), a.size(), reinterpret_cast<const ::UChar*>(b.data()), b.size());
    229 }
    230 
    231 struct StringRange {
    232 public:
    233     StringRange(int pos, int len)
    234         : position(pos)
    235         , length(len)
    236     {
    237     }
    238 
    239     StringRange()
    240     {
    241     }
    242 
    243     int position;
    244     int length;
    245 };
    246 
    247 JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount);
    248 JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount)
    249 {
    250     if (rangeCount == 1 && separatorCount == 0) {
    251         int sourceSize = source.size();
    252         int position = substringRanges[0].position;
    253         int length = substringRanges[0].length;
    254         if (position <= 0 && length >= sourceSize)
    255             return sourceVal;
    256         // We could call UString::substr, but this would result in redundant checks
    257         return jsString(exec, UStringImpl::create(source.rep(), max(0, position), min(sourceSize, length)));
    258     }
    259 
    260     int totalLength = 0;
    261     for (int i = 0; i < rangeCount; i++)
    262         totalLength += substringRanges[i].length;
    263     for (int i = 0; i < separatorCount; i++)
    264         totalLength += separators[i].size();
    265 
    266     if (totalLength == 0)
    267         return jsString(exec, "");
    268 
    269     UChar* buffer;
    270     PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(totalLength, buffer);
    271     if (!impl)
    272         return throwOutOfMemoryError(exec);
    273 
    274     int maxCount = max(rangeCount, separatorCount);
    275     int bufferPos = 0;
    276     for (int i = 0; i < maxCount; i++) {
    277         if (i < rangeCount) {
    278             UStringImpl::copyChars(buffer + bufferPos, source.data() + substringRanges[i].position, substringRanges[i].length);
    279             bufferPos += substringRanges[i].length;
    280         }
    281         if (i < separatorCount) {
    282             UStringImpl::copyChars(buffer + bufferPos, separators[i].data(), separators[i].size());
    283             bufferPos += separators[i].size();
    284         }
    285     }
    286 
    287     return jsString(exec, impl);
    288 }
    289 
    290 JSValue jsReplaceRange(ExecState* exec, const UString& source, int rangeStart, int rangeLength, const UString& replacement);
    291 JSValue jsReplaceRange(ExecState* exec, const UString& source, int rangeStart, int rangeLength, const UString& replacement)
    292 {
    293     int replacementLength = replacement.size();
    294     int totalLength = source.size() - rangeLength + replacementLength;
    295     if (totalLength == 0)
    296         return jsString(exec, "");
    297 
    298     UChar* buffer;
    299     PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(totalLength, buffer);
    300     if (!impl)
    301         return throwOutOfMemoryError(exec);
    302 
    303     UStringImpl::copyChars(buffer, source.data(), rangeStart);
    304     UStringImpl::copyChars(buffer + rangeStart, replacement.data(), replacementLength);
    305     int rangeEnd = rangeStart + rangeLength;
    306     UStringImpl::copyChars(buffer + rangeStart + replacementLength, source.data() + rangeEnd, source.size() - rangeEnd);
    307 
    308     return jsString(exec, impl);
    309 }
    310 
    311 JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    312 {
    313     JSString* sourceVal = thisValue.toThisJSString(exec);
    314     const UString& source = sourceVal->value(exec);
    315 
    316     JSValue pattern = args.at(0);
    317 
    318     JSValue replacement = args.at(1);
    319     UString replacementString;
    320     CallData callData;
    321     CallType callType = replacement.getCallData(callData);
    322     if (callType == CallTypeNone)
    323         replacementString = replacement.toString(exec);
    324 
    325     if (pattern.inherits(&RegExpObject::info)) {
    326         RegExp* reg = asRegExpObject(pattern)->regExp();
    327         bool global = reg->global();
    328 
    329         RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
    330 
    331         int lastIndex = 0;
    332         int startPosition = 0;
    333 
    334         Vector<StringRange, 16> sourceRanges;
    335         Vector<UString, 16> replacements;
    336 
    337         // This is either a loop (if global is set) or a one-way (if not).
    338         if (global && callType == CallTypeJS) {
    339             // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue
    340             int argCount = reg->numSubpatterns() + 1 + 2;
    341             JSFunction* func = asFunction(replacement);
    342             CachedCall cachedCall(exec, func, argCount, exec->exceptionSlot());
    343             if (exec->hadException())
    344                 return jsNull();
    345             while (true) {
    346                 int matchIndex;
    347                 int matchLen = 0;
    348                 int* ovector;
    349                 regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector);
    350                 if (matchIndex < 0)
    351                     break;
    352 
    353                 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
    354 
    355                 int completeMatchStart = ovector[0];
    356                 unsigned i = 0;
    357                 for (; i < reg->numSubpatterns() + 1; ++i) {
    358                     int matchStart = ovector[i * 2];
    359                     int matchLen = ovector[i * 2 + 1] - matchStart;
    360 
    361                     if (matchStart < 0)
    362                         cachedCall.setArgument(i, jsUndefined());
    363                     else
    364                         cachedCall.setArgument(i, jsSubstring(exec, source, matchStart, matchLen));
    365                 }
    366 
    367                 cachedCall.setArgument(i++, jsNumber(exec, completeMatchStart));
    368                 cachedCall.setArgument(i++, sourceVal);
    369 
    370                 cachedCall.setThis(exec->globalThisValue());
    371                 JSValue result = cachedCall.call();
    372                 replacements.append(result.toString(cachedCall.newCallFrame(exec)));
    373                 if (exec->hadException())
    374                     break;
    375 
    376                 lastIndex = matchIndex + matchLen;
    377                 startPosition = lastIndex;
    378 
    379                 // special case of empty match
    380                 if (matchLen == 0) {
    381                     startPosition++;
    382                     if (startPosition > source.size())
    383                         break;
    384                 }
    385             }
    386         } else {
    387             do {
    388                 int matchIndex;
    389                 int matchLen = 0;
    390                 int* ovector;
    391                 regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector);
    392                 if (matchIndex < 0)
    393                     break;
    394 
    395                 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
    396 
    397                 if (callType != CallTypeNone) {
    398                     int completeMatchStart = ovector[0];
    399                     MarkedArgumentBuffer args;
    400 
    401                     for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) {
    402                         int matchStart = ovector[i * 2];
    403                         int matchLen = ovector[i * 2 + 1] - matchStart;
    404 
    405                         if (matchStart < 0)
    406                             args.append(jsUndefined());
    407                         else
    408                             args.append(jsSubstring(exec, source, matchStart, matchLen));
    409                     }
    410 
    411                     args.append(jsNumber(exec, completeMatchStart));
    412                     args.append(sourceVal);
    413 
    414                     replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec));
    415                     if (exec->hadException())
    416                         break;
    417                 } else
    418                     replacements.append(substituteBackreferences(replacementString, source, ovector, reg));
    419 
    420                 lastIndex = matchIndex + matchLen;
    421                 startPosition = lastIndex;
    422 
    423                 // special case of empty match
    424                 if (matchLen == 0) {
    425                     startPosition++;
    426                     if (startPosition > source.size())
    427                         break;
    428                 }
    429             } while (global);
    430         }
    431 
    432         if (!lastIndex && replacements.isEmpty())
    433             return sourceVal;
    434 
    435         if (lastIndex < source.size())
    436             sourceRanges.append(StringRange(lastIndex, source.size() - lastIndex));
    437 
    438         return jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size());
    439     }
    440 
    441     // Not a regular expression, so treat the pattern as a string.
    442 
    443     UString patternString = pattern.toString(exec);
    444     int matchPos = source.find(patternString);
    445 
    446     if (matchPos == -1)
    447         return sourceVal;
    448 
    449     int matchLen = patternString.size();
    450     if (callType != CallTypeNone) {
    451         MarkedArgumentBuffer args;
    452         args.append(jsSubstring(exec, source, matchPos, matchLen));
    453         args.append(jsNumber(exec, matchPos));
    454         args.append(sourceVal);
    455 
    456         replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec);
    457     }
    458 
    459     int ovector[2] = { matchPos, matchPos + matchLen };
    460     return jsReplaceRange(exec, source, matchPos, matchLen, substituteBackreferences(replacementString, source, ovector, 0));
    461 }
    462 
    463 JSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    464 {
    465     // Also used for valueOf.
    466 
    467     if (thisValue.isString())
    468         return thisValue;
    469 
    470     if (thisValue.inherits(&StringObject::info))
    471         return asStringObject(thisValue)->internalValue();
    472 
    473     return throwError(exec, TypeError);
    474 }
    475 
    476 JSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    477 {
    478     UString s = thisValue.toThisString(exec);
    479     unsigned len = s.size();
    480     JSValue a0 = args.at(0);
    481     if (a0.isUInt32()) {
    482         uint32_t i = a0.asUInt32();
    483         if (i < len)
    484             return jsSingleCharacterSubstring(exec, s, i);
    485         return jsEmptyString(exec);
    486     }
    487     double dpos = a0.toInteger(exec);
    488     if (dpos >= 0 && dpos < len)
    489         return jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos));
    490     return jsEmptyString(exec);
    491 }
    492 
    493 JSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    494 {
    495     UString s = thisValue.toThisString(exec);
    496     unsigned len = s.size();
    497     JSValue a0 = args.at(0);
    498     if (a0.isUInt32()) {
    499         uint32_t i = a0.asUInt32();
    500         if (i < len)
    501             return jsNumber(exec, s.data()[i]);
    502         return jsNaN(exec);
    503     }
    504     double dpos = a0.toInteger(exec);
    505     if (dpos >= 0 && dpos < len)
    506         return jsNumber(exec, s[static_cast<int>(dpos)]);
    507     return jsNaN(exec);
    508 }
    509 
    510 JSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    511 {
    512     if (thisValue.isString() && (args.size() == 1)) {
    513         JSValue v = args.at(0);
    514         return v.isString()
    515             ? jsString(exec, asString(thisValue), asString(v))
    516             : jsString(exec, asString(thisValue), v.toString(exec));
    517     }
    518 
    519     return jsString(exec, thisValue, args);
    520 }
    521 
    522 JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    523 {
    524     UString s = thisValue.toThisString(exec);
    525     int len = s.size();
    526 
    527     JSValue a0 = args.at(0);
    528     JSValue a1 = args.at(1);
    529     UString u2 = a0.toString(exec);
    530     int pos;
    531     if (a1.isUndefined())
    532         pos = 0;
    533     else if (a1.isUInt32())
    534         pos = min<uint32_t>(a1.asUInt32(), len);
    535     else {
    536         double dpos = a1.toInteger(exec);
    537         if (dpos < 0)
    538             dpos = 0;
    539         else if (dpos > len)
    540             dpos = len;
    541         pos = static_cast<int>(dpos);
    542     }
    543 
    544     return jsNumber(exec, s.find(u2, pos));
    545 }
    546 
    547 JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    548 {
    549     UString s = thisValue.toThisString(exec);
    550     int len = s.size();
    551 
    552     JSValue a0 = args.at(0);
    553     JSValue a1 = args.at(1);
    554 
    555     UString u2 = a0.toString(exec);
    556     double dpos = a1.toIntegerPreserveNaN(exec);
    557     if (dpos < 0)
    558         dpos = 0;
    559     else if (!(dpos <= len)) // true for NaN
    560         dpos = len;
    561 #if OS(SYMBIAN)
    562     // Work around for broken NaN compare operator
    563     else if (isnan(dpos))
    564         dpos = len;
    565 #endif
    566     return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos)));
    567 }
    568 
    569 JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    570 {
    571     UString s = thisValue.toThisString(exec);
    572 
    573     JSValue a0 = args.at(0);
    574 
    575     UString u = s;
    576     RefPtr<RegExp> reg;
    577     RegExpObject* imp = 0;
    578     if (a0.inherits(&RegExpObject::info))
    579         reg = asRegExpObject(a0)->regExp();
    580     else {
    581         /*
    582          *  ECMA 15.5.4.12 String.prototype.search (regexp)
    583          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
    584          *  replaced with the result of the expression new RegExp(regexp).
    585          */
    586         reg = RegExp::create(&exec->globalData(), a0.toString(exec));
    587     }
    588     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
    589     int pos;
    590     int matchLength = 0;
    591     regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength);
    592     if (!(reg->global())) {
    593         // case without 'g' flag is handled like RegExp.prototype.exec
    594         if (pos < 0)
    595             return jsNull();
    596         return regExpConstructor->arrayOfMatches(exec);
    597     }
    598 
    599     // return array of matches
    600     MarkedArgumentBuffer list;
    601     int lastIndex = 0;
    602     while (pos >= 0) {
    603         list.append(jsSubstring(exec, u, pos, matchLength));
    604         lastIndex = pos;
    605         pos += matchLength == 0 ? 1 : matchLength;
    606         regExpConstructor->performMatch(reg.get(), u, pos, pos, matchLength);
    607     }
    608     if (imp)
    609         imp->setLastIndex(lastIndex);
    610     if (list.isEmpty()) {
    611         // if there are no matches at all, it's important to return
    612         // Null instead of an empty array, because this matches
    613         // other browsers and because Null is a false value.
    614         return jsNull();
    615     }
    616 
    617     return constructArray(exec, list);
    618 }
    619 
    620 JSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    621 {
    622     UString s = thisValue.toThisString(exec);
    623 
    624     JSValue a0 = args.at(0);
    625 
    626     UString u = s;
    627     RefPtr<RegExp> reg;
    628     if (a0.inherits(&RegExpObject::info))
    629         reg = asRegExpObject(a0)->regExp();
    630     else {
    631         /*
    632          *  ECMA 15.5.4.12 String.prototype.search (regexp)
    633          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
    634          *  replaced with the result of the expression new RegExp(regexp).
    635          */
    636         reg = RegExp::create(&exec->globalData(), a0.toString(exec));
    637     }
    638     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
    639     int pos;
    640     int matchLength = 0;
    641     regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength);
    642     return jsNumber(exec, pos);
    643 }
    644 
    645 JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    646 {
    647     UString s = thisValue.toThisString(exec);
    648     int len = s.size();
    649 
    650     JSValue a0 = args.at(0);
    651     JSValue a1 = args.at(1);
    652 
    653     // The arg processing is very much like ArrayProtoFunc::Slice
    654     double start = a0.toInteger(exec);
    655     double end = a1.isUndefined() ? len : a1.toInteger(exec);
    656     double from = start < 0 ? len + start : start;
    657     double to = end < 0 ? len + end : end;
    658     if (to > from && to > 0 && from < len) {
    659         if (from < 0)
    660             from = 0;
    661         if (to > len)
    662             to = len;
    663         return jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from));
    664     }
    665 
    666     return jsEmptyString(exec);
    667 }
    668 
    669 JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    670 {
    671     UString s = thisValue.toThisString(exec);
    672 
    673     JSValue a0 = args.at(0);
    674     JSValue a1 = args.at(1);
    675 
    676     JSArray* result = constructEmptyArray(exec);
    677     unsigned i = 0;
    678     int p0 = 0;
    679     unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec);
    680     if (a0.inherits(&RegExpObject::info)) {
    681         RegExp* reg = asRegExpObject(a0)->regExp();
    682         if (s.isEmpty() && reg->match(s, 0) >= 0) {
    683             // empty string matched by regexp -> empty array
    684             return result;
    685         }
    686         int pos = 0;
    687         while (i != limit && pos < s.size()) {
    688             Vector<int, 32> ovector;
    689             int mpos = reg->match(s, pos, &ovector);
    690             if (mpos < 0)
    691                 break;
    692             int mlen = ovector[1] - ovector[0];
    693             pos = mpos + (mlen == 0 ? 1 : mlen);
    694             if (mpos != p0 || mlen) {
    695                 result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0));
    696                 p0 = mpos + mlen;
    697             }
    698             for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) {
    699                 int spos = ovector[si * 2];
    700                 if (spos < 0)
    701                     result->put(exec, i++, jsUndefined());
    702                 else
    703                     result->put(exec, i++, jsSubstring(exec, s, spos, ovector[si * 2 + 1] - spos));
    704             }
    705         }
    706     } else {
    707         UString u2 = a0.toString(exec);
    708         if (u2.isEmpty()) {
    709             if (s.isEmpty()) {
    710                 // empty separator matches empty string -> empty array
    711                 return result;
    712             }
    713             while (i != limit && p0 < s.size() - 1)
    714                 result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++));
    715         } else {
    716             int pos;
    717             while (i != limit && (pos = s.find(u2, p0)) >= 0) {
    718                 result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0));
    719                 p0 = pos + u2.size();
    720             }
    721         }
    722     }
    723 
    724     // add remaining string
    725     if (i != limit)
    726         result->put(exec, i++, jsSubstring(exec, s, p0, s.size() - p0));
    727 
    728     return result;
    729 }
    730 
    731 JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    732 {
    733     UString s = thisValue.toThisString(exec);
    734     int len = s.size();
    735 
    736     JSValue a0 = args.at(0);
    737     JSValue a1 = args.at(1);
    738 
    739     double start = a0.toInteger(exec);
    740     double length = a1.isUndefined() ? len : a1.toInteger(exec);
    741     if (start >= len || length <= 0)
    742         return jsEmptyString(exec);
    743     if (start < 0) {
    744         start += len;
    745         if (start < 0)
    746             start = 0;
    747     }
    748     if (start + length > len)
    749         length = len - start;
    750     return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length));
    751 }
    752 
    753 JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    754 {
    755     UString s = thisValue.toThisString(exec);
    756     int len = s.size();
    757 
    758     JSValue a0 = args.at(0);
    759     JSValue a1 = args.at(1);
    760 
    761     double start = a0.toNumber(exec);
    762     double end = a1.toNumber(exec);
    763     if (isnan(start))
    764         start = 0;
    765     if (isnan(end))
    766         end = 0;
    767     if (start < 0)
    768         start = 0;
    769     if (end < 0)
    770         end = 0;
    771     if (start > len)
    772         start = len;
    773     if (end > len)
    774         end = len;
    775     if (a1.isUndefined())
    776         end = len;
    777     if (start > end) {
    778         double temp = end;
    779         end = start;
    780         start = temp;
    781     }
    782     return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(end) - static_cast<unsigned>(start));
    783 }
    784 
    785 JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    786 {
    787     JSString* sVal = thisValue.toThisJSString(exec);
    788     const UString& s = sVal->value(exec);
    789 
    790     int sSize = s.size();
    791     if (!sSize)
    792         return sVal;
    793 
    794     const UChar* sData = s.data();
    795     Vector<UChar> buffer(sSize);
    796 
    797     UChar ored = 0;
    798     for (int i = 0; i < sSize; i++) {
    799         UChar c = sData[i];
    800         ored |= c;
    801         buffer[i] = toASCIILower(c);
    802     }
    803     if (!(ored & ~0x7f))
    804         return jsString(exec, UString::adopt(buffer));
    805 
    806     bool error;
    807     int length = Unicode::toLower(buffer.data(), sSize, sData, sSize, &error);
    808     if (error) {
    809         buffer.resize(length);
    810         length = Unicode::toLower(buffer.data(), length, sData, sSize, &error);
    811         if (error)
    812             return sVal;
    813     }
    814     if (length == sSize) {
    815         if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
    816             return sVal;
    817     } else
    818         buffer.resize(length);
    819     return jsString(exec, UString::adopt(buffer));
    820 }
    821 
    822 JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    823 {
    824     JSString* sVal = thisValue.toThisJSString(exec);
    825     const UString& s = sVal->value(exec);
    826 
    827     int sSize = s.size();
    828     if (!sSize)
    829         return sVal;
    830 
    831     const UChar* sData = s.data();
    832     Vector<UChar> buffer(sSize);
    833 
    834     UChar ored = 0;
    835     for (int i = 0; i < sSize; i++) {
    836         UChar c = sData[i];
    837         ored |= c;
    838         buffer[i] = toASCIIUpper(c);
    839     }
    840     if (!(ored & ~0x7f))
    841         return jsString(exec, UString::adopt(buffer));
    842 
    843     bool error;
    844     int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error);
    845     if (error) {
    846         buffer.resize(length);
    847         length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error);
    848         if (error)
    849             return sVal;
    850     }
    851     if (length == sSize) {
    852         if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
    853             return sVal;
    854     } else
    855         buffer.resize(length);
    856     return jsString(exec, UString::adopt(buffer));
    857 }
    858 
    859 JSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    860 {
    861     if (args.size() < 1)
    862       return jsNumber(exec, 0);
    863 
    864     UString s = thisValue.toThisString(exec);
    865     JSValue a0 = args.at(0);
    866     return jsNumber(exec, localeCompare(s, a0.toString(exec)));
    867 }
    868 
    869 JSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    870 {
    871     UString s = thisValue.toThisString(exec);
    872     return jsMakeNontrivialString(exec, "<big>", s, "</big>");
    873 }
    874 
    875 JSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    876 {
    877     UString s = thisValue.toThisString(exec);
    878     return jsMakeNontrivialString(exec, "<small>", s, "</small>");
    879 }
    880 
    881 JSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    882 {
    883     UString s = thisValue.toThisString(exec);
    884     return jsMakeNontrivialString(exec, "<blink>", s, "</blink>");
    885 }
    886 
    887 JSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    888 {
    889     UString s = thisValue.toThisString(exec);
    890     return jsMakeNontrivialString(exec, "<b>", s, "</b>");
    891 }
    892 
    893 JSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    894 {
    895     UString s = thisValue.toThisString(exec);
    896     return jsMakeNontrivialString(exec, "<tt>", s, "</tt>");
    897 }
    898 
    899 JSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    900 {
    901     UString s = thisValue.toThisString(exec);
    902     return jsMakeNontrivialString(exec, "<i>", s, "</i>");
    903 }
    904 
    905 JSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    906 {
    907     UString s = thisValue.toThisString(exec);
    908     return jsMakeNontrivialString(exec, "<strike>", s, "</strike>");
    909 }
    910 
    911 JSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    912 {
    913     UString s = thisValue.toThisString(exec);
    914     return jsMakeNontrivialString(exec, "<sub>", s, "</sub>");
    915 }
    916 
    917 JSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
    918 {
    919     UString s = thisValue.toThisString(exec);
    920     return jsMakeNontrivialString(exec, "<sup>", s, "</sup>");
    921 }
    922 
    923 JSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    924 {
    925     UString s = thisValue.toThisString(exec);
    926     JSValue a0 = args.at(0);
    927     return jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>");
    928 }
    929 
    930 JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    931 {
    932     UString s = thisValue.toThisString(exec);
    933     JSValue a0 = args.at(0);
    934 
    935     uint32_t smallInteger;
    936     if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
    937         unsigned stringSize = s.size();
    938         unsigned bufferSize = 22 + stringSize;
    939         UChar* buffer;
    940         PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(bufferSize, buffer);
    941         if (!impl)
    942             return jsUndefined();
    943         buffer[0] = '<';
    944         buffer[1] = 'f';
    945         buffer[2] = 'o';
    946         buffer[3] = 'n';
    947         buffer[4] = 't';
    948         buffer[5] = ' ';
    949         buffer[6] = 's';
    950         buffer[7] = 'i';
    951         buffer[8] = 'z';
    952         buffer[9] = 'e';
    953         buffer[10] = '=';
    954         buffer[11] = '"';
    955         buffer[12] = '0' + smallInteger;
    956         buffer[13] = '"';
    957         buffer[14] = '>';
    958         memcpy(&buffer[15], s.data(), stringSize * sizeof(UChar));
    959         buffer[15 + stringSize] = '<';
    960         buffer[16 + stringSize] = '/';
    961         buffer[17 + stringSize] = 'f';
    962         buffer[18 + stringSize] = 'o';
    963         buffer[19 + stringSize] = 'n';
    964         buffer[20 + stringSize] = 't';
    965         buffer[21 + stringSize] = '>';
    966         return jsNontrivialString(exec, impl);
    967     }
    968 
    969     return jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>");
    970 }
    971 
    972 JSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    973 {
    974     UString s = thisValue.toThisString(exec);
    975     JSValue a0 = args.at(0);
    976     return jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>");
    977 }
    978 
    979 JSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args)
    980 {
    981     UString s = thisValue.toThisString(exec);
    982     JSValue a0 = args.at(0);
    983     UString linkText = a0.toString(exec);
    984 
    985     unsigned linkTextSize = linkText.size();
    986     unsigned stringSize = s.size();
    987     unsigned bufferSize = 15 + linkTextSize + stringSize;
    988     UChar* buffer;
    989     PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(bufferSize, buffer);
    990     if (!impl)
    991         return jsUndefined();
    992     buffer[0] = '<';
    993     buffer[1] = 'a';
    994     buffer[2] = ' ';
    995     buffer[3] = 'h';
    996     buffer[4] = 'r';
    997     buffer[5] = 'e';
    998     buffer[6] = 'f';
    999     buffer[7] = '=';
   1000     buffer[8] = '"';
   1001     memcpy(&buffer[9], linkText.data(), linkTextSize * sizeof(UChar));
   1002     buffer[9 + linkTextSize] = '"';
   1003     buffer[10 + linkTextSize] = '>';
   1004     memcpy(&buffer[11 + linkTextSize], s.data(), stringSize * sizeof(UChar));
   1005     buffer[11 + linkTextSize + stringSize] = '<';
   1006     buffer[12 + linkTextSize + stringSize] = '/';
   1007     buffer[13 + linkTextSize + stringSize] = 'a';
   1008     buffer[14 + linkTextSize + stringSize] = '>';
   1009     return jsNontrivialString(exec, impl);
   1010 }
   1011 
   1012 enum {
   1013     TrimLeft = 1,
   1014     TrimRight = 2
   1015 };
   1016 
   1017 static inline bool isTrimWhitespace(UChar c)
   1018 {
   1019     return isStrWhiteSpace(c) || c == 0x200b;
   1020 }
   1021 
   1022 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
   1023 {
   1024     UString str = thisValue.toThisString(exec);
   1025     int left = 0;
   1026     if (trimKind & TrimLeft) {
   1027         while (left < str.size() && isTrimWhitespace(str[left]))
   1028             left++;
   1029     }
   1030     int right = str.size();
   1031     if (trimKind & TrimRight) {
   1032         while (right > left && isTrimWhitespace(str[right - 1]))
   1033             right--;
   1034     }
   1035 
   1036     // Don't gc allocate a new string if we don't have to.
   1037     if (left == 0 && right == str.size() && thisValue.isString())
   1038         return thisValue;
   1039 
   1040     return jsString(exec, str.substr(left, right - left));
   1041 }
   1042 
   1043 JSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
   1044 {
   1045     return trimString(exec, thisValue, TrimLeft | TrimRight);
   1046 }
   1047 
   1048 JSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
   1049 {
   1050     return trimString(exec, thisValue, TrimLeft);
   1051 }
   1052 
   1053 JSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&)
   1054 {
   1055     return trimString(exec, thisValue, TrimRight);
   1056 }
   1057 
   1058 
   1059 } // namespace JSC
   1060