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.
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.
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.
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
23 #include "StringPrototype.h"
25 #include "CachedCall.h"
27 #include "Executable.h"
28 #include "JSGlobalObjectFunctions.h"
30 #include "JSFunction.h"
31 #include "JSStringBuilder.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>
47 ASSERT_CLASS_FITS_IN_CELL(StringPrototype);
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*);
84 #include "StringPrototype.lut.h"
88 const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, 0, ExecState::stringTable, CREATE_METHOD_TABLE(StringPrototype) };
90 /* Source for StringPrototype.lut.h
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
110 # toLocaleLowerCase and toLocaleUpperCase are currently identical to toLowerCase and toUpperCase
111 toLocaleLowerCase stringProtoFuncToLowerCase DontEnum|Function 0
112 toLocaleUpperCase stringProtoFuncToUpperCase DontEnum|Function 0
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
134 StringPrototype::StringPrototype(ExecState* exec, Structure* structure)
135 : StringObject(exec->globalData(), structure)
139 void StringPrototype::finishCreation(ExecState* exec, JSGlobalObject*, JSString* nameAndMessage)
141 Base::finishCreation(exec->globalData(), nameAndMessage);
142 ASSERT(inherits(&s_info));
144 // The constructor will be added later, after StringConstructor has been built
145 putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
148 bool StringPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot)
150 return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), jsCast<StringPrototype*>(cell), propertyName, slot);
153 bool StringPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
155 return getStaticFunctionDescriptor<StringObject>(exec, ExecState::stringTable(exec), jsCast<StringPrototype*>(object), propertyName, descriptor);
158 // ------------------------------ Functions --------------------------
160 // Helper for producing a JSString for 'string', where 'string' was been produced by
161 // calling ToString on 'originalValue'. In cases where 'originalValue' already was a
162 // string primitive we can just use this, otherwise we need to allocate a new JSString.
163 static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const UString& string)
165 if (originalValue.isString()) {
166 ASSERT(asString(originalValue)->value(exec) == string);
167 return asString(originalValue);
169 return jsString(exec, string);
172 static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, size_t i)
174 Vector<UChar> substitutedReplacement;
177 if (i + 1 == replacement.length())
180 UChar ref = replacement[i + 1];
184 substitutedReplacement.append(replacement.characters() + offset, i - offset);
193 backrefStart = ovector[0];
194 backrefLength = ovector[1] - backrefStart;
195 } else if (ref == '`') {
197 backrefLength = ovector[0];
198 } else if (ref == '\'') {
199 backrefStart = ovector[1];
200 backrefLength = source.length() - backrefStart;
201 } else if (reg && ref >= '0' && ref <= '9') {
202 // 1- and 2-digit back references are allowed
203 unsigned backrefIndex = ref - '0';
204 if (backrefIndex > reg->numSubpatterns())
206 if (replacement.length() > i + 2) {
207 ref = replacement[i + 2];
208 if (ref >= '0' && ref <= '9') {
209 backrefIndex = 10 * backrefIndex + ref - '0';
210 if (backrefIndex > reg->numSubpatterns())
211 backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
218 backrefStart = ovector[2 * backrefIndex];
219 backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
224 substitutedReplacement.append(replacement.characters() + offset, i - offset);
227 if (backrefStart >= 0)
228 substitutedReplacement.append(source.characters() + backrefStart, backrefLength);
229 } while ((i = replacement.find('$', i + 1)) != notFound);
231 if (replacement.length() - offset)
232 substitutedReplacement.append(replacement.characters() + offset, replacement.length() - offset);
234 substitutedReplacement.shrinkToFit();
235 return UString::adopt(substitutedReplacement);
238 static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg)
240 size_t i = replacement.find('$', 0);
241 if (UNLIKELY(i != notFound))
242 return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
246 static inline int localeCompare(const UString& a, const UString& b)
248 return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.characters()), a.length(), reinterpret_cast<const ::UChar*>(b.characters()), b.length());
253 StringRange(int pos, int len)
267 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount)
269 if (rangeCount == 1) {
270 int sourceSize = source.length();
271 int position = substringRanges[0].position;
272 int length = substringRanges[0].length;
273 if (position <= 0 && length >= sourceSize)
275 // We could call UString::substr, but this would result in redundant checks
276 return jsString(exec, StringImpl::create(source.impl(), max(0, position), min(sourceSize, length)));
280 for (int i = 0; i < rangeCount; i++)
281 totalLength += substringRanges[i].length;
284 return jsString(exec, "");
286 if (source.is8Bit()) {
288 const LChar* sourceData = source.characters8();
289 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
291 return throwOutOfMemoryError(exec);
294 for (int i = 0; i < rangeCount; i++) {
295 if (int srcLen = substringRanges[i].length) {
296 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
301 return jsString(exec, impl.release());
305 const UChar* sourceData = source.characters16();
307 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
309 return throwOutOfMemoryError(exec);
312 for (int i = 0; i < rangeCount; i++) {
313 if (int srcLen = substringRanges[i].length) {
314 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
319 return jsString(exec, impl.release());
322 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount)
324 if (rangeCount == 1 && separatorCount == 0) {
325 int sourceSize = source.length();
326 int position = substringRanges[0].position;
327 int length = substringRanges[0].length;
328 if (position <= 0 && length >= sourceSize)
330 // We could call UString::substr, but this would result in redundant checks
331 return jsString(exec, StringImpl::create(source.impl(), max(0, position), min(sourceSize, length)));
335 bool allSeperators8Bit = true;
336 for (int i = 0; i < rangeCount; i++)
337 totalLength += substringRanges[i].length;
338 for (int i = 0; i < separatorCount; i++) {
339 totalLength += separators[i].length();
340 if (separators[i].length() && !separators[i].is8Bit())
341 allSeperators8Bit = false;
345 return jsString(exec, "");
347 if (source.is8Bit() && allSeperators8Bit) {
349 const LChar* sourceData = source.characters8();
351 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
353 return throwOutOfMemoryError(exec);
355 int maxCount = max(rangeCount, separatorCount);
357 for (int i = 0; i < maxCount; i++) {
358 if (i < rangeCount) {
359 if (int srcLen = substringRanges[i].length) {
360 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
364 if (i < separatorCount) {
365 if (int sepLen = separators[i].length()) {
366 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
372 return jsString(exec, impl.release());
376 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
378 return throwOutOfMemoryError(exec);
380 int maxCount = max(rangeCount, separatorCount);
382 for (int i = 0; i < maxCount; i++) {
383 if (i < rangeCount) {
384 if (int srcLen = substringRanges[i].length) {
385 StringImpl::copyChars(buffer + bufferPos, source.characters() + substringRanges[i].position, srcLen);
389 if (i < separatorCount) {
390 if (int sepLen = separators[i].length()) {
391 StringImpl::copyChars(buffer + bufferPos, separators[i].characters(), sepLen);
397 return jsString(exec, impl.release());
400 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
402 JSValue thisValue = exec->hostThisValue();
403 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
404 return throwVMTypeError(exec);
405 JSString* sourceVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec));
406 JSValue pattern = exec->argument(0);
407 JSValue replacement = exec->argument(1);
408 JSGlobalData* globalData = &exec->globalData();
410 if (pattern.inherits(&RegExpObject::s_info)) {
411 UString replacementString;
413 CallType callType = getCallData(replacement, callData);
414 if (callType == CallTypeNone)
415 replacementString = replacement.toString(exec);
417 const UString& source = sourceVal->value(exec);
418 unsigned sourceLen = source.length();
419 if (exec->hadException())
420 return JSValue::encode(JSValue());
421 RegExp* reg = asRegExpObject(pattern)->regExp();
422 bool global = reg->global();
424 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
426 // Optimization for substring removal (replace with empty).
427 if (global && callType == CallTypeNone && !replacementString.length()) {
429 unsigned startPosition = 0;
431 Vector<StringRange, 16> sourceRanges;
437 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
441 if (lastIndex < matchIndex)
442 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
444 lastIndex = matchIndex + matchLen;
445 startPosition = lastIndex;
447 // special case of empty match
450 if (startPosition > sourceLen)
456 return JSValue::encode(sourceVal);
458 if (static_cast<unsigned>(lastIndex) < sourceLen)
459 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
461 return JSValue::encode(jsSpliceSubstrings(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size()));
465 unsigned startPosition = 0;
467 Vector<StringRange, 16> sourceRanges;
468 Vector<UString, 16> replacements;
470 // This is either a loop (if global is set) or a one-way (if not).
471 if (global && callType == CallTypeJS) {
472 // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue
473 int argCount = reg->numSubpatterns() + 1 + 2;
474 JSFunction* func = asFunction(replacement);
475 CachedCall cachedCall(exec, func, argCount);
476 if (exec->hadException())
477 return JSValue::encode(jsNull());
478 if (source.is8Bit()) {
483 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
487 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
489 int completeMatchStart = ovector[0];
491 for (; i < reg->numSubpatterns() + 1; ++i) {
492 int matchStart = ovector[i * 2];
493 int matchLen = ovector[i * 2 + 1] - matchStart;
496 cachedCall.setArgument(i, jsUndefined());
498 cachedCall.setArgument(i, jsSubstring8(globalData, source, matchStart, matchLen));
501 cachedCall.setArgument(i++, jsNumber(completeMatchStart));
502 cachedCall.setArgument(i++, sourceVal);
504 cachedCall.setThis(jsUndefined());
505 JSValue result = cachedCall.call();
506 if (LIKELY(result.isString()))
507 replacements.append(asString(result)->value(exec));
509 replacements.append(result.toString(cachedCall.newCallFrame(exec)));
510 if (exec->hadException())
513 lastIndex = matchIndex + matchLen;
514 startPosition = lastIndex;
516 // special case of empty match
519 if (startPosition > sourceLen)
528 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
532 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
534 int completeMatchStart = ovector[0];
536 for (; i < reg->numSubpatterns() + 1; ++i) {
537 int matchStart = ovector[i * 2];
538 int matchLen = ovector[i * 2 + 1] - matchStart;
541 cachedCall.setArgument(i, jsUndefined());
543 cachedCall.setArgument(i, jsSubstring(globalData, source, matchStart, matchLen));
546 cachedCall.setArgument(i++, jsNumber(completeMatchStart));
547 cachedCall.setArgument(i++, sourceVal);
549 cachedCall.setThis(jsUndefined());
550 JSValue result = cachedCall.call();
551 if (LIKELY(result.isString()))
552 replacements.append(asString(result)->value(exec));
554 replacements.append(result.toString(cachedCall.newCallFrame(exec)));
555 if (exec->hadException())
558 lastIndex = matchIndex + matchLen;
559 startPosition = lastIndex;
561 // special case of empty match
564 if (startPosition > sourceLen)
574 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
578 if (callType != CallTypeNone) {
579 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
581 int completeMatchStart = ovector[0];
582 MarkedArgumentBuffer args;
584 for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) {
585 int matchStart = ovector[i * 2];
586 int matchLen = ovector[i * 2 + 1] - matchStart;
589 args.append(jsUndefined());
591 args.append(jsSubstring(exec, source, matchStart, matchLen));
594 args.append(jsNumber(completeMatchStart));
595 args.append(sourceVal);
597 replacements.append(call(exec, replacement, callType, callData, jsUndefined(), args).toString(exec));
598 if (exec->hadException())
601 int replLen = replacementString.length();
602 if (lastIndex < matchIndex || replLen) {
603 sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
606 replacements.append(substituteBackreferences(replacementString, source, ovector, reg));
608 replacements.append(UString());
612 lastIndex = matchIndex + matchLen;
613 startPosition = lastIndex;
615 // special case of empty match
618 if (startPosition > sourceLen)
624 if (!lastIndex && replacements.isEmpty())
625 return JSValue::encode(sourceVal);
627 if (static_cast<unsigned>(lastIndex) < sourceLen)
628 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
630 return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
633 // Not a regular expression, so treat the pattern as a string.
635 // 'patternString' (or 'searchValue', as it is referred to in the spec) is converted before the replacement.
636 UString patternString = pattern.toString(exec);
637 if (exec->hadException())
638 return JSValue::encode(jsUndefined());
640 UString replacementString;
642 CallType callType = getCallData(replacement, callData);
643 if (callType == CallTypeNone)
644 replacementString = replacement.toString(exec);
645 if (exec->hadException())
646 return JSValue::encode(jsUndefined());
648 // Special case for single character patterns without back reference replacement
649 if (patternString.length() == 1 && callType == CallTypeNone && replacementString.find('$', 0) == notFound)
650 return JSValue::encode(sourceVal->replaceCharacter(exec, patternString[0], replacementString));
652 const UString& source = sourceVal->value(exec);
653 size_t matchPos = source.find(patternString);
655 if (matchPos == notFound)
656 return JSValue::encode(sourceVal);
658 int matchLen = patternString.length();
659 if (callType != CallTypeNone) {
660 MarkedArgumentBuffer args;
661 args.append(jsSubstring(exec, source, matchPos, matchLen));
662 args.append(jsNumber(matchPos));
663 args.append(sourceVal);
665 replacementString = call(exec, replacement, callType, callData, jsUndefined(), args).toString(exec);
668 size_t matchEnd = matchPos + matchLen;
669 int ovector[2] = { matchPos, matchEnd };
670 return JSValue::encode(jsString(exec, source.substringSharingImpl(0, matchPos), substituteBackreferences(replacementString, source, ovector, 0), source.substringSharingImpl(matchEnd)));
673 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
675 JSValue thisValue = exec->hostThisValue();
676 // Also used for valueOf.
678 if (thisValue.isString())
679 return JSValue::encode(thisValue);
681 if (thisValue.inherits(&StringObject::s_info))
682 return JSValue::encode(asStringObject(thisValue)->internalValue());
684 return throwVMTypeError(exec);
687 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
689 JSValue thisValue = exec->hostThisValue();
690 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
691 return throwVMTypeError(exec);
692 UString s = thisValue.toString(exec);
693 unsigned len = s.length();
694 JSValue a0 = exec->argument(0);
696 uint32_t i = a0.asUInt32();
698 return JSValue::encode(jsSingleCharacterSubstring(exec, s, i));
699 return JSValue::encode(jsEmptyString(exec));
701 double dpos = a0.toInteger(exec);
702 if (dpos >= 0 && dpos < len)
703 return JSValue::encode(jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)));
704 return JSValue::encode(jsEmptyString(exec));
707 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
709 JSValue thisValue = exec->hostThisValue();
710 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
711 return throwVMTypeError(exec);
712 UString s = thisValue.toString(exec);
713 unsigned len = s.length();
714 JSValue a0 = exec->argument(0);
716 uint32_t i = a0.asUInt32();
719 return JSValue::encode(jsNumber(s.characters8()[i]));
720 return JSValue::encode(jsNumber(s.characters16()[i]));
722 return JSValue::encode(jsNaN());
724 double dpos = a0.toInteger(exec);
725 if (dpos >= 0 && dpos < len)
726 return JSValue::encode(jsNumber(s[static_cast<int>(dpos)]));
727 return JSValue::encode(jsNaN());
730 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
732 JSValue thisValue = exec->hostThisValue();
733 if (thisValue.isString() && (exec->argumentCount() == 1)) {
734 JSValue v = exec->argument(0);
735 return JSValue::encode(v.isString()
736 ? jsString(exec, asString(thisValue), asString(v))
737 : jsString(exec, asString(thisValue), jsString(&exec->globalData(), v.toString(exec))));
739 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
740 return throwVMTypeError(exec);
741 return JSValue::encode(jsStringFromArguments(exec, thisValue));
744 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
746 JSValue thisValue = exec->hostThisValue();
747 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
748 return throwVMTypeError(exec);
749 UString s = thisValue.toString(exec);
750 int len = s.length();
752 JSValue a0 = exec->argument(0);
753 JSValue a1 = exec->argument(1);
754 UString u2 = a0.toString(exec);
756 if (a1.isUndefined())
758 else if (a1.isUInt32())
759 pos = min<uint32_t>(a1.asUInt32(), len);
761 double dpos = a1.toInteger(exec);
766 pos = static_cast<int>(dpos);
769 size_t result = s.find(u2, pos);
770 if (result == notFound)
771 return JSValue::encode(jsNumber(-1));
772 return JSValue::encode(jsNumber(result));
775 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
777 JSValue thisValue = exec->hostThisValue();
778 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
779 return throwVMTypeError(exec);
780 UString s = thisValue.toString(exec);
781 int len = s.length();
783 JSValue a0 = exec->argument(0);
784 JSValue a1 = exec->argument(1);
786 UString u2 = a0.toString(exec);
787 double dpos = a1.toIntegerPreserveNaN(exec);
790 else if (!(dpos <= len)) // true for NaN
793 size_t result = s.reverseFind(u2, static_cast<unsigned>(dpos));
794 if (result == notFound)
795 return JSValue::encode(jsNumber(-1));
796 return JSValue::encode(jsNumber(result));
799 EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
801 JSValue thisValue = exec->hostThisValue();
802 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
803 return throwVMTypeError(exec);
804 UString s = thisValue.toString(exec);
805 JSGlobalData* globalData = &exec->globalData();
807 JSValue a0 = exec->argument(0);
810 if (a0.inherits(&RegExpObject::s_info))
811 reg = asRegExpObject(a0)->regExp();
814 * ECMA 15.5.4.12 String.prototype.search (regexp)
815 * If regexp is not an object whose [[Class]] property is "RegExp", it is
816 * replaced with the result of the expression new RegExp(regexp).
817 * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
819 reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec), NoFlags);
821 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
824 regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
825 if (!(reg->global())) {
826 // case without 'g' flag is handled like RegExp.prototype.exec
828 return JSValue::encode(jsNull());
829 return JSValue::encode(regExpConstructor->arrayOfMatches(exec));
832 // return array of matches
833 MarkedArgumentBuffer list;
835 list.append(jsSubstring(exec, s, pos, matchLength));
836 pos += matchLength == 0 ? 1 : matchLength;
837 regExpConstructor->performMatch(*globalData, reg, s, pos, pos, matchLength);
839 if (list.isEmpty()) {
840 // if there are no matches at all, it's important to return
841 // Null instead of an empty array, because this matches
842 // other browsers and because Null is a false value.
843 return JSValue::encode(jsNull());
846 return JSValue::encode(constructArray(exec, list));
849 EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec)
851 JSValue thisValue = exec->hostThisValue();
852 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
853 return throwVMTypeError(exec);
854 UString s = thisValue.toString(exec);
855 JSGlobalData* globalData = &exec->globalData();
857 JSValue a0 = exec->argument(0);
860 if (a0.inherits(&RegExpObject::s_info))
861 reg = asRegExpObject(a0)->regExp();
864 * ECMA 15.5.4.12 String.prototype.search (regexp)
865 * If regexp is not an object whose [[Class]] property is "RegExp", it is
866 * replaced with the result of the expression new RegExp(regexp).
867 * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
869 reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec), NoFlags);
871 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
874 regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
875 return JSValue::encode(jsNumber(pos));
878 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
880 JSValue thisValue = exec->hostThisValue();
881 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
882 return throwVMTypeError(exec);
883 UString s = thisValue.toString(exec);
884 int len = s.length();
886 JSValue a0 = exec->argument(0);
887 JSValue a1 = exec->argument(1);
889 // The arg processing is very much like ArrayProtoFunc::Slice
890 double start = a0.toInteger(exec);
891 double end = a1.isUndefined() ? len : a1.toInteger(exec);
892 double from = start < 0 ? len + start : start;
893 double to = end < 0 ? len + end : end;
894 if (to > from && to > 0 && from < len) {
899 return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
902 return JSValue::encode(jsEmptyString(exec));
905 // ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit)
906 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
908 // 1. Call CheckObjectCoercible passing the this value as its argument.
909 JSValue thisValue = exec->hostThisValue();
910 if (thisValue.isUndefinedOrNull())
911 return throwVMTypeError(exec);
913 // 2. Let S be the result of calling ToString, giving it the this value as its argument.
914 // 6. Let s be the number of characters in S.
915 UString input = thisValue.toString(exec);
917 // 3. Let A be a new array created as if by the expression new Array()
918 // where Array is the standard built-in constructor with that name.
919 JSArray* result = constructEmptyArray(exec);
921 // 4. Let lengthA be 0.
922 unsigned resultLength = 0;
924 // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
925 JSValue limitValue = exec->argument(1);
926 unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
931 // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
932 // otherwise let R = ToString(separator).
933 JSValue separatorValue = exec->argument(0);
934 if (separatorValue.inherits(&RegExpObject::s_info)) {
935 JSGlobalData* globalData = &exec->globalData();
936 RegExp* reg = asRegExpObject(separatorValue)->regExp();
938 // 9. If lim == 0, return A.
940 return JSValue::encode(result);
942 // 10. If separator is undefined, then
943 if (separatorValue.isUndefined()) {
944 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
945 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
946 result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
948 return JSValue::encode(result);
951 // 11. If s == 0, then
952 if (input.isEmpty()) {
953 // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
954 // b. If z is not failure, return A.
955 // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
956 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
958 if (reg->match(*globalData, input, 0) < 0)
959 result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
960 return JSValue::encode(result);
964 size_t matchPosition = 0;
965 // 13. Repeat, while q != s
966 while (matchPosition < input.length()) {
967 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
968 Vector<int, 32> ovector;
969 int mpos = reg->match(*globalData, input, matchPosition, &ovector);
970 // b. If z is failure, then let q = q + 1.
973 matchPosition = mpos;
975 // c. Else, z is not failure
976 // i. z must be a State. Let e be z's endIndex and let cap be z's captures array.
977 size_t matchEnd = ovector[1];
979 // ii. If e == p, then let q = q + 1.
980 if (matchEnd == position) {
986 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
987 // through q (exclusive).
988 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
989 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
990 result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
991 // 3. Increment lengthA by 1.
992 // 4. If lengthA == lim, return A.
993 if (++resultLength == limit)
994 return JSValue::encode(result);
999 matchPosition = matchEnd;
1002 // 7. Repeat, while i is not equal to the number of elements in cap.
1004 for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) {
1005 // b Call the [[DefineOwnProperty]] internal method of A with arguments
1006 // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]:
1007 // true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1008 int sub = ovector[i * 2];
1009 result->methodTable()->putByIndex(result, exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub));
1010 // c Increment lengthA by 1.
1011 // d If lengthA == lim, return A.
1012 if (++resultLength == limit)
1013 return JSValue::encode(result);
1017 UString separator = separatorValue.toString(exec);
1019 // 9. If lim == 0, return A.
1021 return JSValue::encode(result);
1023 // 10. If separator is undefined, then
1024 JSValue separatorValue = exec->argument(0);
1025 if (separatorValue.isUndefined()) {
1026 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1027 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1028 result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
1030 return JSValue::encode(result);
1033 // 11. If s == 0, then
1034 if (input.isEmpty()) {
1035 // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
1036 // b. If z is not failure, return A.
1037 // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1038 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1040 if (!separator.isEmpty())
1041 result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
1042 return JSValue::encode(result);
1045 // Optimized case for splitting on the empty string.
1046 if (separator.isEmpty()) {
1047 limit = std::min(limit, input.length());
1048 // Zero limt/input length handled in steps 9/11 respectively, above.
1052 result->methodTable()->putByIndex(result, exec, position, jsSingleCharacterSubstring(exec, input, position));
1053 } while (++position < limit);
1055 return JSValue::encode(result);
1059 size_t matchPosition;
1060 // 13. Repeat, while q != s
1061 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1062 // b. If z is failure, then let q = q+1.
1063 // c. Else, z is not failure
1064 while ((matchPosition = input.find(separator, position)) != notFound) {
1065 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1066 // through q (exclusive).
1067 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1068 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1069 result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
1070 // 3. Increment lengthA by 1.
1071 // 4. If lengthA == lim, return A.
1072 if (++resultLength == limit)
1073 return JSValue::encode(result);
1077 position = matchPosition + separator.length();
1081 // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1082 // through s (exclusive).
1083 // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor
1084 // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1085 result->methodTable()->putByIndex(result, exec, resultLength++, jsSubstring(exec, input, position, input.length() - position));
1088 return JSValue::encode(result);
1091 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1093 JSValue thisValue = exec->hostThisValue();
1095 JSString* jsString = 0;
1097 if (thisValue.isString()) {
1098 jsString = static_cast<JSString*>(thisValue.asCell());
1099 len = jsString->length();
1100 } else if (thisValue.isUndefinedOrNull()) {
1101 // CheckObjectCoercible
1102 return throwVMTypeError(exec);
1104 uString = thisValue.toString(exec);
1105 if (exec->hadException())
1106 return JSValue::encode(jsUndefined());
1107 len = uString.length();
1110 JSValue a0 = exec->argument(0);
1111 JSValue a1 = exec->argument(1);
1113 double start = a0.toInteger(exec);
1114 double length = a1.isUndefined() ? len : a1.toInteger(exec);
1115 if (start >= len || length <= 0)
1116 return JSValue::encode(jsEmptyString(exec));
1122 if (start + length > len)
1123 length = len - start;
1124 unsigned substringStart = static_cast<unsigned>(start);
1125 unsigned substringLength = static_cast<unsigned>(length);
1127 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1128 return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1131 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1133 JSValue thisValue = exec->hostThisValue();
1135 JSString* jsString = 0;
1137 if (thisValue.isString()) {
1138 jsString = static_cast<JSString*>(thisValue.asCell());
1139 len = jsString->length();
1140 } else if (thisValue.isUndefinedOrNull()) {
1141 // CheckObjectCoercible
1142 return throwVMTypeError(exec);
1144 uString = thisValue.toString(exec);
1145 if (exec->hadException())
1146 return JSValue::encode(jsUndefined());
1147 len = uString.length();
1150 JSValue a0 = exec->argument(0);
1151 JSValue a1 = exec->argument(1);
1153 double start = a0.toNumber(exec);
1155 if (!(start >= 0)) // check for negative values or NaN
1157 else if (start > len)
1159 if (a1.isUndefined())
1162 end = a1.toNumber(exec);
1163 if (!(end >= 0)) // check for negative values or NaN
1173 unsigned substringStart = static_cast<unsigned>(start);
1174 unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1176 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1177 return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1180 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1182 JSValue thisValue = exec->hostThisValue();
1183 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1184 return throwVMTypeError(exec);
1185 JSString* sVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec));
1186 const UString& s = sVal->value(exec);
1188 int sSize = s.length();
1190 return JSValue::encode(sVal);
1192 StringImpl* ourImpl = s.impl();
1193 RefPtr<StringImpl> lower = ourImpl->lower();
1194 if (ourImpl == lower.get())
1195 return JSValue::encode(sVal);
1196 return JSValue::encode(jsString(exec, UString(lower.release())));
1199 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1201 JSValue thisValue = exec->hostThisValue();
1202 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1203 return throwVMTypeError(exec);
1204 JSString* sVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec));
1205 const UString& s = sVal->value(exec);
1207 int sSize = s.length();
1209 return JSValue::encode(sVal);
1211 const UChar* sData = s.characters();
1212 Vector<UChar> buffer(sSize);
1215 for (int i = 0; i < sSize; i++) {
1218 buffer[i] = toASCIIUpper(c);
1220 if (!(ored & ~0x7f))
1221 return JSValue::encode(jsString(exec, UString::adopt(buffer)));
1224 int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error);
1226 buffer.resize(length);
1227 length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error);
1229 return JSValue::encode(sVal);
1231 if (length == sSize) {
1232 if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
1233 return JSValue::encode(sVal);
1235 buffer.resize(length);
1236 return JSValue::encode(jsString(exec, UString::adopt(buffer)));
1239 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1241 if (exec->argumentCount() < 1)
1242 return JSValue::encode(jsNumber(0));
1244 JSValue thisValue = exec->hostThisValue();
1245 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1246 return throwVMTypeError(exec);
1247 UString s = thisValue.toString(exec);
1249 JSValue a0 = exec->argument(0);
1250 return JSValue::encode(jsNumber(localeCompare(s, a0.toString(exec))));
1253 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1255 JSValue thisValue = exec->hostThisValue();
1256 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1257 return throwVMTypeError(exec);
1258 UString s = thisValue.toString(exec);
1259 return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1262 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1264 JSValue thisValue = exec->hostThisValue();
1265 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1266 return throwVMTypeError(exec);
1267 UString s = thisValue.toString(exec);
1268 return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1271 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1273 JSValue thisValue = exec->hostThisValue();
1274 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1275 return throwVMTypeError(exec);
1276 UString s = thisValue.toString(exec);
1277 return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1280 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1282 JSValue thisValue = exec->hostThisValue();
1283 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1284 return throwVMTypeError(exec);
1285 UString s = thisValue.toString(exec);
1286 return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1289 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1291 JSValue thisValue = exec->hostThisValue();
1292 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1293 return throwVMTypeError(exec);
1294 UString s = thisValue.toString(exec);
1295 return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1298 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1300 JSValue thisValue = exec->hostThisValue();
1301 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1302 return throwVMTypeError(exec);
1303 UString s = thisValue.toString(exec);
1304 return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1307 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1309 JSValue thisValue = exec->hostThisValue();
1310 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1311 return throwVMTypeError(exec);
1312 UString s = thisValue.toString(exec);
1313 return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1316 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1318 JSValue thisValue = exec->hostThisValue();
1319 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1320 return throwVMTypeError(exec);
1321 UString s = thisValue.toString(exec);
1322 return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1325 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1327 JSValue thisValue = exec->hostThisValue();
1328 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1329 return throwVMTypeError(exec);
1330 UString s = thisValue.toString(exec);
1331 return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1334 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1336 JSValue thisValue = exec->hostThisValue();
1337 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1338 return throwVMTypeError(exec);
1339 UString s = thisValue.toString(exec);
1340 JSValue a0 = exec->argument(0);
1341 return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>"));
1344 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1346 JSValue thisValue = exec->hostThisValue();
1347 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1348 return throwVMTypeError(exec);
1349 UString s = thisValue.toString(exec);
1350 JSValue a0 = exec->argument(0);
1352 uint32_t smallInteger;
1353 if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1354 unsigned stringSize = s.length();
1355 unsigned bufferSize = 22 + stringSize;
1357 PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1359 return JSValue::encode(jsUndefined());
1372 buffer[12] = '0' + smallInteger;
1375 memcpy(&buffer[15], s.characters(), stringSize * sizeof(UChar));
1376 buffer[15 + stringSize] = '<';
1377 buffer[16 + stringSize] = '/';
1378 buffer[17 + stringSize] = 'f';
1379 buffer[18 + stringSize] = 'o';
1380 buffer[19 + stringSize] = 'n';
1381 buffer[20 + stringSize] = 't';
1382 buffer[21 + stringSize] = '>';
1383 return JSValue::encode(jsNontrivialString(exec, impl));
1386 return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>"));
1389 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1391 JSValue thisValue = exec->hostThisValue();
1392 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1393 return throwVMTypeError(exec);
1394 UString s = thisValue.toString(exec);
1395 JSValue a0 = exec->argument(0);
1396 return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>"));
1399 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1401 JSValue thisValue = exec->hostThisValue();
1402 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1403 return throwVMTypeError(exec);
1404 UString s = thisValue.toString(exec);
1405 JSValue a0 = exec->argument(0);
1406 UString linkText = a0.toString(exec);
1408 unsigned linkTextSize = linkText.length();
1409 unsigned stringSize = s.length();
1410 unsigned bufferSize = 15 + linkTextSize + stringSize;
1412 PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1414 return JSValue::encode(jsUndefined());
1424 memcpy(&buffer[9], linkText.characters(), linkTextSize * sizeof(UChar));
1425 buffer[9 + linkTextSize] = '"';
1426 buffer[10 + linkTextSize] = '>';
1427 memcpy(&buffer[11 + linkTextSize], s.characters(), stringSize * sizeof(UChar));
1428 buffer[11 + linkTextSize + stringSize] = '<';
1429 buffer[12 + linkTextSize + stringSize] = '/';
1430 buffer[13 + linkTextSize + stringSize] = 'a';
1431 buffer[14 + linkTextSize + stringSize] = '>';
1432 return JSValue::encode(jsNontrivialString(exec, impl));
1440 static inline bool isTrimWhitespace(UChar c)
1442 return isStrWhiteSpace(c) || c == 0x200b;
1445 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1447 if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1448 return throwTypeError(exec);
1449 UString str = thisValue.toString(exec);
1451 if (trimKind & TrimLeft) {
1452 while (left < str.length() && isTrimWhitespace(str[left]))
1455 unsigned right = str.length();
1456 if (trimKind & TrimRight) {
1457 while (right > left && isTrimWhitespace(str[right - 1]))
1461 // Don't gc allocate a new string if we don't have to.
1462 if (left == 0 && right == str.length() && thisValue.isString())
1465 return jsString(exec, str.substringSharingImpl(left, right - left));
1468 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1470 JSValue thisValue = exec->hostThisValue();
1471 return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1474 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1476 JSValue thisValue = exec->hostThisValue();
1477 return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1480 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1482 JSValue thisValue = exec->hostThisValue();
1483 return JSValue::encode(trimString(exec, thisValue, TrimRight));