• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
StringPrototype(ExecState * exec,JSGlobalObject * globalObject,Structure * structure)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 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)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 
getOwnPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & descriptor)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 
substituteBackreferencesSlow(const UString & replacement,const UString & source,const int * ovector,RegExp * reg,size_t i)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 
substituteBackreferences(const UString & replacement,const UString & source,const int * ovector,RegExp * reg)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 
localeCompare(const UString & a,const UString & b)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:
StringRangeJSC::StringRange237     StringRange(int pos, int len)
238         : position(pos)
239         , length(len)
240     {
241     }
242 
StringRangeJSC::StringRange243     StringRange()
244     {
245     }
246 
247     int position;
248     int length;
249 };
250 
jsSpliceSubstringsWithSeparators(ExecState * exec,JSString * sourceVal,const UString & source,const StringRange * substringRanges,int rangeCount,const UString * separators,int separatorCount)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 
stringProtoFuncReplace(ExecState * exec)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 
stringProtoFuncToString(ExecState * exec)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 
stringProtoFuncCharAt(ExecState * exec)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 
stringProtoFuncCharCodeAt(ExecState * exec)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 
stringProtoFuncConcat(ExecState * exec)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 
stringProtoFuncIndexOf(ExecState * exec)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 
stringProtoFuncLastIndexOf(ExecState * exec)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 
stringProtoFuncMatch(ExecState * exec)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 
stringProtoFuncSearch(ExecState * exec)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 
stringProtoFuncSlice(ExecState * exec)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 
stringProtoFuncSplit(ExecState * exec)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 
stringProtoFuncSubstr(ExecState * exec)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 
stringProtoFuncSubstring(ExecState * exec)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 
stringProtoFuncToLowerCase(ExecState * exec)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 
stringProtoFuncToUpperCase(ExecState * exec)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 
stringProtoFuncLocaleCompare(ExecState * exec)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 
stringProtoFuncBig(ExecState * exec)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 
stringProtoFuncSmall(ExecState * exec)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 
stringProtoFuncBlink(ExecState * exec)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 
stringProtoFuncBold(ExecState * exec)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 
stringProtoFuncFixed(ExecState * exec)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 
stringProtoFuncItalics(ExecState * exec)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 
stringProtoFuncStrike(ExecState * exec)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 
stringProtoFuncSub(ExecState * exec)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 
stringProtoFuncSup(ExecState * exec)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 
stringProtoFuncFontcolor(ExecState * exec)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 
stringProtoFuncFontsize(ExecState * exec)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 
stringProtoFuncAnchor(ExecState * exec)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 
stringProtoFuncLink(ExecState * exec)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 
isTrimWhitespace(UChar c)1105 static inline bool isTrimWhitespace(UChar c)
1106 {
1107     return isStrWhiteSpace(c) || c == 0x200b;
1108 }
1109 
trimString(ExecState * exec,JSValue thisValue,int trimKind)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 
stringProtoFuncTrim(ExecState * exec)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 
stringProtoFuncTrimLeft(ExecState * exec)1139 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1140 {
1141     JSValue thisValue = exec->hostThisValue();
1142     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1143 }
1144 
stringProtoFuncTrimRight(ExecState * exec)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