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