e70b38836ca31368fee486f79f1fd52fc8b95263
[WebKit-https.git] / Source / JavaScriptCore / runtime / StringPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@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     JSGlobalData* globalData = &exec->globalData();
304
305     UString replacementString;
306     CallData callData;
307     CallType callType = getCallData(replacement, callData);
308     if (callType == CallTypeNone)
309         replacementString = replacement.toString(exec);
310
311     if (pattern.inherits(&RegExpObject::s_info)) {
312         const UString& source = sourceVal->value(exec);
313         unsigned sourceLen = source.length();
314         if (exec->hadException())
315             return JSValue::encode(JSValue());
316         RegExp* reg = asRegExpObject(pattern)->regExp();
317         bool global = reg->global();
318
319         RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
320
321         int lastIndex = 0;
322         unsigned startPosition = 0;
323
324         Vector<StringRange, 16> sourceRanges;
325         Vector<UString, 16> replacements;
326
327         // This is either a loop (if global is set) or a one-way (if not).
328         if (global && callType == CallTypeJS) {
329             // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue
330             int argCount = reg->numSubpatterns() + 1 + 2;
331             JSFunction* func = asFunction(replacement);
332             CachedCall cachedCall(exec, func, argCount);
333             if (exec->hadException())
334                 return JSValue::encode(jsNull());
335             while (true) {
336                 int matchIndex;
337                 int matchLen = 0;
338                 int* ovector;
339                 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
340                 if (matchIndex < 0)
341                     break;
342
343                 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
344
345                 int completeMatchStart = ovector[0];
346                 unsigned i = 0;
347                 for (; i < reg->numSubpatterns() + 1; ++i) {
348                     int matchStart = ovector[i * 2];
349                     int matchLen = ovector[i * 2 + 1] - matchStart;
350
351                     if (matchStart < 0)
352                         cachedCall.setArgument(i, jsUndefined());
353                     else
354                         cachedCall.setArgument(i, jsSubstring(exec, source, matchStart, matchLen));
355                 }
356
357                 cachedCall.setArgument(i++, jsNumber(completeMatchStart));
358                 cachedCall.setArgument(i++, sourceVal);
359
360                 cachedCall.setThis(exec->globalThisValue());
361                 JSValue result = cachedCall.call();
362                 if (LIKELY(result.isString()))
363                     replacements.append(asString(result)->value(exec));
364                 else
365                     replacements.append(result.toString(cachedCall.newCallFrame(exec)));
366                 if (exec->hadException())
367                     break;
368
369                 lastIndex = matchIndex + matchLen;
370                 startPosition = lastIndex;
371
372                 // special case of empty match
373                 if (matchLen == 0) {
374                     startPosition++;
375                     if (startPosition > sourceLen)
376                         break;
377                 }
378             }
379         } else {
380             do {
381                 int matchIndex;
382                 int matchLen = 0;
383                 int* ovector;
384                 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
385                 if (matchIndex < 0)
386                     break;
387
388                 if (callType != CallTypeNone) {
389                     sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
390
391                     int completeMatchStart = ovector[0];
392                     MarkedArgumentBuffer args;
393
394                     for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) {
395                         int matchStart = ovector[i * 2];
396                         int matchLen = ovector[i * 2 + 1] - matchStart;
397  
398                         if (matchStart < 0)
399                             args.append(jsUndefined());
400                         else
401                             args.append(jsSubstring(exec, source, matchStart, matchLen));
402                     }
403
404                     args.append(jsNumber(completeMatchStart));
405                     args.append(sourceVal);
406
407                     replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec));
408                     if (exec->hadException())
409                         break;
410                 } else {
411                     int replLen = replacementString.length();
412                     if (lastIndex < matchIndex || replLen) {
413                         sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
414  
415                         if (replLen)
416                             replacements.append(substituteBackreferences(replacementString, source, ovector, reg));
417                         else
418                             replacements.append(UString());
419                     }
420                 }
421
422                 lastIndex = matchIndex + matchLen;
423                 startPosition = lastIndex;
424
425                 // special case of empty match
426                 if (matchLen == 0) {
427                     startPosition++;
428                     if (startPosition > sourceLen)
429                         break;
430                 }
431             } while (global);
432         }
433
434         if (!lastIndex && replacements.isEmpty())
435             return JSValue::encode(sourceVal);
436
437         if (static_cast<unsigned>(lastIndex) < sourceLen)
438             sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
439
440         return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
441     }
442
443     // Not a regular expression, so treat the pattern as a string.
444
445     UString patternString = pattern.toString(exec);
446     // Special case for single character patterns without back reference replacement
447     if (patternString.length() == 1 && callType == CallTypeNone && replacementString.find('$', 0) == notFound)
448         return JSValue::encode(sourceVal->replaceCharacter(exec, patternString[0], replacementString));
449
450     const UString& source = sourceVal->value(exec);
451     size_t matchPos = source.find(patternString);
452
453     if (matchPos == notFound)
454         return JSValue::encode(sourceVal);
455
456     int matchLen = patternString.length();
457     if (callType != CallTypeNone) {
458         MarkedArgumentBuffer args;
459         args.append(jsSubstring(exec, source, matchPos, matchLen));
460         args.append(jsNumber(matchPos));
461         args.append(sourceVal);
462
463         replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec);
464     }
465     
466     size_t matchEnd = matchPos + matchLen;
467     int ovector[2] = { matchPos, matchEnd };
468     return JSValue::encode(jsString(exec, source.substringSharingImpl(0, matchPos), substituteBackreferences(replacementString, source, ovector, 0), source.substringSharingImpl(matchEnd)));
469 }
470
471 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
472 {
473     JSValue thisValue = exec->hostThisValue();
474     // Also used for valueOf.
475
476     if (thisValue.isString())
477         return JSValue::encode(thisValue);
478
479     if (thisValue.inherits(&StringObject::s_info))
480         return JSValue::encode(asStringObject(thisValue)->internalValue());
481
482     return throwVMTypeError(exec);
483 }
484
485 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
486 {
487     JSValue thisValue = exec->hostThisValue();
488     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
489         return throwVMTypeError(exec);
490     UString s = thisValue.toThisString(exec);
491     unsigned len = s.length();
492     JSValue a0 = exec->argument(0);
493     if (a0.isUInt32()) {
494         uint32_t i = a0.asUInt32();
495         if (i < len)
496             return JSValue::encode(jsSingleCharacterSubstring(exec, s, i));
497         return JSValue::encode(jsEmptyString(exec));
498     }
499     double dpos = a0.toInteger(exec);
500     if (dpos >= 0 && dpos < len)
501         return JSValue::encode(jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)));
502     return JSValue::encode(jsEmptyString(exec));
503 }
504
505 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
506 {
507     JSValue thisValue = exec->hostThisValue();
508     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
509         return throwVMTypeError(exec);
510     UString s = thisValue.toThisString(exec);
511     unsigned len = s.length();
512     JSValue a0 = exec->argument(0);
513     if (a0.isUInt32()) {
514         uint32_t i = a0.asUInt32();
515         if (i < len)
516             return JSValue::encode(jsNumber(s.characters()[i]));
517         return JSValue::encode(jsNaN());
518     }
519     double dpos = a0.toInteger(exec);
520     if (dpos >= 0 && dpos < len)
521         return JSValue::encode(jsNumber(s[static_cast<int>(dpos)]));
522     return JSValue::encode(jsNaN());
523 }
524
525 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
526 {
527     JSValue thisValue = exec->hostThisValue();
528     if (thisValue.isString() && (exec->argumentCount() == 1)) {
529         JSValue v = exec->argument(0);
530         return JSValue::encode(v.isString()
531             ? jsString(exec, asString(thisValue), asString(v))
532             : jsString(exec, asString(thisValue), v.toString(exec)));
533     }
534     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
535         return throwVMTypeError(exec);
536     return JSValue::encode(jsString(exec, thisValue));
537 }
538
539 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
540 {
541     JSValue thisValue = exec->hostThisValue();
542     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
543         return throwVMTypeError(exec);
544     UString s = thisValue.toThisString(exec);
545     int len = s.length();
546
547     JSValue a0 = exec->argument(0);
548     JSValue a1 = exec->argument(1);
549     UString u2 = a0.toString(exec);
550     int pos;
551     if (a1.isUndefined())
552         pos = 0;
553     else if (a1.isUInt32())
554         pos = min<uint32_t>(a1.asUInt32(), len);
555     else {
556         double dpos = a1.toInteger(exec);
557         if (dpos < 0)
558             dpos = 0;
559         else if (dpos > len)
560             dpos = len;
561         pos = static_cast<int>(dpos);
562     }
563
564     size_t result = s.find(u2, pos);
565     if (result == notFound)
566         return JSValue::encode(jsNumber(-1));
567     return JSValue::encode(jsNumber(result));
568 }
569
570 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
571 {
572     JSValue thisValue = exec->hostThisValue();
573     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
574         return throwVMTypeError(exec);
575     UString s = thisValue.toThisString(exec);
576     int len = s.length();
577
578     JSValue a0 = exec->argument(0);
579     JSValue a1 = exec->argument(1);
580
581     UString u2 = a0.toString(exec);
582     double dpos = a1.toIntegerPreserveNaN(exec);
583     if (dpos < 0)
584         dpos = 0;
585     else if (!(dpos <= len)) // true for NaN
586         dpos = len;
587 #if OS(SYMBIAN)
588     // Work around for broken NaN compare operator
589     else if (isnan(dpos))
590         dpos = len;
591 #endif
592
593     size_t result = s.reverseFind(u2, static_cast<unsigned>(dpos));
594     if (result == notFound)
595         return JSValue::encode(jsNumber(-1));
596     return JSValue::encode(jsNumber(result));
597 }
598
599 EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
600 {
601     JSValue thisValue = exec->hostThisValue();
602     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
603         return throwVMTypeError(exec);
604     UString s = thisValue.toThisString(exec);
605     JSGlobalData* globalData = &exec->globalData();
606
607     JSValue a0 = exec->argument(0);
608
609     RegExp* reg;
610     if (a0.inherits(&RegExpObject::s_info))
611         reg = asRegExpObject(a0)->regExp();
612     else {
613         /*
614          *  ECMA 15.5.4.12 String.prototype.search (regexp)
615          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
616          *  replaced with the result of the expression new RegExp(regexp).
617          */
618         reg = exec->globalData().regExpCache()->lookupOrCreate(a0.toString(exec), NoFlags);
619     }
620     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
621     int pos;
622     int matchLength = 0;
623     regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
624     if (!(reg->global())) {
625         // case without 'g' flag is handled like RegExp.prototype.exec
626         if (pos < 0)
627             return JSValue::encode(jsNull());
628         return JSValue::encode(regExpConstructor->arrayOfMatches(exec));
629     }
630
631     // return array of matches
632     MarkedArgumentBuffer list;
633     while (pos >= 0) {
634         list.append(jsSubstring(exec, s, pos, matchLength));
635         pos += matchLength == 0 ? 1 : matchLength;
636         regExpConstructor->performMatch(*globalData, reg, 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     JSGlobalData* globalData = &exec->globalData();
655
656     JSValue a0 = exec->argument(0);
657
658     RegExp* reg;
659     if (a0.inherits(&RegExpObject::s_info))
660         reg = asRegExpObject(a0)->regExp();
661     else { 
662         /*
663          *  ECMA 15.5.4.12 String.prototype.search (regexp)
664          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
665          *  replaced with the result of the expression new RegExp(regexp).
666          */
667         reg = exec->globalData().regExpCache()->lookupOrCreate(a0.toString(exec), NoFlags);
668     }
669     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
670     int pos;
671     int matchLength = 0;
672     regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
673     return JSValue::encode(jsNumber(pos));
674 }
675
676 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
677 {
678     JSValue thisValue = exec->hostThisValue();
679     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
680         return throwVMTypeError(exec);
681     UString s = thisValue.toThisString(exec);
682     int len = s.length();
683
684     JSValue a0 = exec->argument(0);
685     JSValue a1 = exec->argument(1);
686
687     // The arg processing is very much like ArrayProtoFunc::Slice
688     double start = a0.toInteger(exec);
689     double end = a1.isUndefined() ? len : a1.toInteger(exec);
690     double from = start < 0 ? len + start : start;
691     double to = end < 0 ? len + end : end;
692     if (to > from && to > 0 && from < len) {
693         if (from < 0)
694             from = 0;
695         if (to > len)
696             to = len;
697         return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
698     }
699
700     return JSValue::encode(jsEmptyString(exec));
701 }
702
703 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
704 {
705     JSValue thisValue = exec->hostThisValue();
706     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
707         return throwVMTypeError(exec);
708     UString s = thisValue.toThisString(exec);
709     JSGlobalData* globalData = &exec->globalData();
710
711     JSValue a0 = exec->argument(0);
712     JSValue a1 = exec->argument(1);
713
714     JSArray* result = constructEmptyArray(exec);
715     unsigned i = 0;
716     unsigned p0 = 0;
717     unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec);
718     if (a0.inherits(&RegExpObject::s_info)) {
719         RegExp* reg = asRegExpObject(a0)->regExp();
720         if (s.isEmpty() && reg->match(*globalData, s, 0) >= 0) {
721             // empty string matched by regexp -> empty array
722             return JSValue::encode(result);
723         }
724         unsigned pos = 0;
725         while (i != limit && pos < s.length()) {
726             Vector<int, 32> ovector;
727             int mpos = reg->match(*globalData, s, pos, &ovector);
728             if (mpos < 0)
729                 break;
730             int mlen = ovector[1] - ovector[0];
731             pos = mpos + (mlen == 0 ? 1 : mlen);
732             if (static_cast<unsigned>(mpos) != p0 || mlen) {
733                 result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0));
734                 p0 = mpos + mlen;
735             }
736             for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) {
737                 int spos = ovector[si * 2];
738                 if (spos < 0)
739                     result->put(exec, i++, jsUndefined());
740                 else
741                     result->put(exec, i++, jsSubstring(exec, s, spos, ovector[si * 2 + 1] - spos));
742             }
743         }
744     } else {
745         UString u2 = a0.toString(exec);
746         if (u2.isEmpty()) {
747             if (s.isEmpty()) {
748                 // empty separator matches empty string -> empty array
749                 return JSValue::encode(result);
750             }
751             while (i != limit && p0 < s.length() - 1)
752                 result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++));
753         } else {
754             size_t pos;
755             while (i != limit && (pos = s.find(u2, p0)) != notFound) {
756                 result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0));
757                 p0 = pos + u2.length();
758             }
759         }
760     }
761
762     // add remaining string
763     if (i != limit)
764         result->put(exec, i++, jsSubstring(exec, s, p0, s.length() - p0));
765
766     return JSValue::encode(result);
767 }
768
769 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
770 {
771     JSValue thisValue = exec->hostThisValue();
772     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
773         return throwVMTypeError(exec);
774     unsigned len;
775     JSString* jsString = 0;
776     UString uString;
777     if (thisValue.isString()) {
778         jsString = static_cast<JSString*>(thisValue.asCell());
779         len = jsString->length();
780     } else {
781         uString = thisValue.toThisObject(exec)->toString(exec);
782         len = uString.length();
783     }
784
785     JSValue a0 = exec->argument(0);
786     JSValue a1 = exec->argument(1);
787
788     double start = a0.toInteger(exec);
789     double length = a1.isUndefined() ? len : a1.toInteger(exec);
790     if (start >= len || length <= 0)
791         return JSValue::encode(jsEmptyString(exec));
792     if (start < 0) {
793         start += len;
794         if (start < 0)
795             start = 0;
796     }
797     if (start + length > len)
798         length = len - start;
799     unsigned substringStart = static_cast<unsigned>(start);
800     unsigned substringLength = static_cast<unsigned>(length);
801     if (jsString)
802         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
803     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
804 }
805
806 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
807 {
808     JSValue thisValue = exec->hostThisValue();
809     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
810         return throwVMTypeError(exec);
811     int len;
812     JSString* jsString = 0;
813     UString uString;
814     if (thisValue.isString()) {
815         jsString = static_cast<JSString*>(thisValue.asCell());
816         len = jsString->length();
817     } else {
818         uString = thisValue.toThisObject(exec)->toString(exec);
819         len = uString.length();
820     }
821
822     JSValue a0 = exec->argument(0);
823     JSValue a1 = exec->argument(1);
824
825     double start = a0.toNumber(exec);
826     double end;
827     if (!(start >= 0)) // check for negative values or NaN
828         start = 0;
829     else if (start > len)
830         start = len;
831     if (a1.isUndefined())
832         end = len;
833     else { 
834         end = a1.toNumber(exec);
835         if (!(end >= 0)) // check for negative values or NaN
836             end = 0;
837         else if (end > len)
838             end = len;
839     }
840     if (start > end) {
841         double temp = end;
842         end = start;
843         start = temp;
844     }
845     unsigned substringStart = static_cast<unsigned>(start);
846     unsigned substringLength = static_cast<unsigned>(end) - substringStart;
847     if (jsString)
848         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
849     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
850 }
851
852 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
853 {
854     JSValue thisValue = exec->hostThisValue();
855     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
856         return throwVMTypeError(exec);
857     JSString* sVal = thisValue.toThisJSString(exec);
858     const UString& s = sVal->value(exec);
859
860     int sSize = s.length();
861     if (!sSize)
862         return JSValue::encode(sVal);
863
864     const UChar* sData = s.characters();
865     Vector<UChar> buffer(sSize);
866
867     UChar ored = 0;
868     for (int i = 0; i < sSize; i++) {
869         UChar c = sData[i];
870         ored |= c;
871         buffer[i] = toASCIILower(c);
872     }
873     if (!(ored & ~0x7f))
874         return JSValue::encode(jsString(exec, UString::adopt(buffer)));
875
876     bool error;
877     int length = Unicode::toLower(buffer.data(), sSize, sData, sSize, &error);
878     if (error) {
879         buffer.resize(length);
880         length = Unicode::toLower(buffer.data(), length, sData, sSize, &error);
881         if (error)
882             return JSValue::encode(sVal);
883     }
884     if (length == sSize) {
885         if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
886             return JSValue::encode(sVal);
887     } else
888         buffer.resize(length);
889     return JSValue::encode(jsString(exec, UString::adopt(buffer)));
890 }
891
892 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
893 {
894     JSValue thisValue = exec->hostThisValue();
895     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
896         return throwVMTypeError(exec);
897     JSString* sVal = thisValue.toThisJSString(exec);
898     const UString& s = sVal->value(exec);
899
900     int sSize = s.length();
901     if (!sSize)
902         return JSValue::encode(sVal);
903
904     const UChar* sData = s.characters();
905     Vector<UChar> buffer(sSize);
906
907     UChar ored = 0;
908     for (int i = 0; i < sSize; i++) {
909         UChar c = sData[i];
910         ored |= c;
911         buffer[i] = toASCIIUpper(c);
912     }
913     if (!(ored & ~0x7f))
914         return JSValue::encode(jsString(exec, UString::adopt(buffer)));
915
916     bool error;
917     int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error);
918     if (error) {
919         buffer.resize(length);
920         length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error);
921         if (error)
922             return JSValue::encode(sVal);
923     }
924     if (length == sSize) {
925         if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
926             return JSValue::encode(sVal);
927     } else
928         buffer.resize(length);
929     return JSValue::encode(jsString(exec, UString::adopt(buffer)));
930 }
931
932 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
933 {
934     if (exec->argumentCount() < 1)
935       return JSValue::encode(jsNumber(0));
936
937     JSValue thisValue = exec->hostThisValue();
938     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
939         return throwVMTypeError(exec);
940
941     UString s = thisValue.toThisString(exec);
942     JSValue a0 = exec->argument(0);
943     return JSValue::encode(jsNumber(localeCompare(s, a0.toString(exec))));
944 }
945
946 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
947 {
948     JSValue thisValue = exec->hostThisValue();
949     UString s = thisValue.toThisString(exec);
950     return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
951 }
952
953 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
954 {
955     JSValue thisValue = exec->hostThisValue();
956     UString s = thisValue.toThisString(exec);
957     return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
958 }
959
960 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
961 {
962     JSValue thisValue = exec->hostThisValue();
963     UString s = thisValue.toThisString(exec);
964     return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
965 }
966
967 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
968 {
969     JSValue thisValue = exec->hostThisValue();
970     UString s = thisValue.toThisString(exec);
971     return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
972 }
973
974 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
975 {
976     JSValue thisValue = exec->hostThisValue();
977     UString s = thisValue.toThisString(exec);
978     return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
979 }
980
981 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
982 {
983     JSValue thisValue = exec->hostThisValue();
984     UString s = thisValue.toThisString(exec);
985     return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
986 }
987
988 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
989 {
990     JSValue thisValue = exec->hostThisValue();
991     UString s = thisValue.toThisString(exec);
992     return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
993 }
994
995 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
996 {
997     JSValue thisValue = exec->hostThisValue();
998     UString s = thisValue.toThisString(exec);
999     return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1000 }
1001
1002 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1003 {
1004     JSValue thisValue = exec->hostThisValue();
1005     UString s = thisValue.toThisString(exec);
1006     return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1007 }
1008
1009 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1010 {
1011     JSValue thisValue = exec->hostThisValue();
1012     UString s = thisValue.toThisString(exec);
1013     JSValue a0 = exec->argument(0);
1014     return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>"));
1015 }
1016
1017 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1018 {
1019     JSValue thisValue = exec->hostThisValue();
1020     UString s = thisValue.toThisString(exec);
1021     JSValue a0 = exec->argument(0);
1022
1023     uint32_t smallInteger;
1024     if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1025         unsigned stringSize = s.length();
1026         unsigned bufferSize = 22 + stringSize;
1027         UChar* buffer;
1028         PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1029         if (!impl)
1030             return JSValue::encode(jsUndefined());
1031         buffer[0] = '<';
1032         buffer[1] = 'f';
1033         buffer[2] = 'o';
1034         buffer[3] = 'n';
1035         buffer[4] = 't';
1036         buffer[5] = ' ';
1037         buffer[6] = 's';
1038         buffer[7] = 'i';
1039         buffer[8] = 'z';
1040         buffer[9] = 'e';
1041         buffer[10] = '=';
1042         buffer[11] = '"';
1043         buffer[12] = '0' + smallInteger;
1044         buffer[13] = '"';
1045         buffer[14] = '>';
1046         memcpy(&buffer[15], s.characters(), stringSize * sizeof(UChar));
1047         buffer[15 + stringSize] = '<';
1048         buffer[16 + stringSize] = '/';
1049         buffer[17 + stringSize] = 'f';
1050         buffer[18 + stringSize] = 'o';
1051         buffer[19 + stringSize] = 'n';
1052         buffer[20 + stringSize] = 't';
1053         buffer[21 + stringSize] = '>';
1054         return JSValue::encode(jsNontrivialString(exec, impl));
1055     }
1056
1057     return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>"));
1058 }
1059
1060 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1061 {
1062     JSValue thisValue = exec->hostThisValue();
1063     UString s = thisValue.toThisString(exec);
1064     JSValue a0 = exec->argument(0);
1065     return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>"));
1066 }
1067
1068 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1069 {
1070     JSValue thisValue = exec->hostThisValue();
1071     UString s = thisValue.toThisString(exec);
1072     JSValue a0 = exec->argument(0);
1073     UString linkText = a0.toString(exec);
1074
1075     unsigned linkTextSize = linkText.length();
1076     unsigned stringSize = s.length();
1077     unsigned bufferSize = 15 + linkTextSize + stringSize;
1078     UChar* buffer;
1079     PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1080     if (!impl)
1081         return JSValue::encode(jsUndefined());
1082     buffer[0] = '<';
1083     buffer[1] = 'a';
1084     buffer[2] = ' ';
1085     buffer[3] = 'h';
1086     buffer[4] = 'r';
1087     buffer[5] = 'e';
1088     buffer[6] = 'f';
1089     buffer[7] = '=';
1090     buffer[8] = '"';
1091     memcpy(&buffer[9], linkText.characters(), linkTextSize * sizeof(UChar));
1092     buffer[9 + linkTextSize] = '"';
1093     buffer[10 + linkTextSize] = '>';
1094     memcpy(&buffer[11 + linkTextSize], s.characters(), stringSize * sizeof(UChar));
1095     buffer[11 + linkTextSize + stringSize] = '<';
1096     buffer[12 + linkTextSize + stringSize] = '/';
1097     buffer[13 + linkTextSize + stringSize] = 'a';
1098     buffer[14 + linkTextSize + stringSize] = '>';
1099     return JSValue::encode(jsNontrivialString(exec, impl));
1100 }
1101
1102 enum {
1103     TrimLeft = 1,
1104     TrimRight = 2
1105 };
1106
1107 static inline bool isTrimWhitespace(UChar c)
1108 {
1109     return isStrWhiteSpace(c) || c == 0x200b;
1110 }
1111
1112 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1113 {
1114     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1115         return throwTypeError(exec);
1116     UString str = thisValue.toThisString(exec);
1117     unsigned left = 0;
1118     if (trimKind & TrimLeft) {
1119         while (left < str.length() && isTrimWhitespace(str[left]))
1120             left++;
1121     }
1122     unsigned right = str.length();
1123     if (trimKind & TrimRight) {
1124         while (right > left && isTrimWhitespace(str[right - 1]))
1125             right--;
1126     }
1127
1128     // Don't gc allocate a new string if we don't have to.
1129     if (left == 0 && right == str.length() && thisValue.isString())
1130         return thisValue;
1131
1132     return jsString(exec, str.substringSharingImpl(left, right - left));
1133 }
1134
1135 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1136 {
1137     JSValue thisValue = exec->hostThisValue();
1138     return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1139 }
1140
1141 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1142 {
1143     JSValue thisValue = exec->hostThisValue();
1144     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1145 }
1146
1147 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1148 {
1149     JSValue thisValue = exec->hostThisValue();
1150     return JSValue::encode(trimString(exec, thisValue, TrimRight));
1151 }
1152     
1153     
1154 } // namespace JSC