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