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