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