1 /* 2 * Copyright (C) 1999-2001 Harri Porten (porten (at) 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 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 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 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 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 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 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: 237 StringRange(int pos, int len) 238 : position(pos) 239 , length(len) 240 { 241 } 242 243 StringRange() 244 { 245 } 246 247 int position; 248 int length; 249 }; 250 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1105 static inline bool isTrimWhitespace(UChar c) 1106 { 1107 return isStrWhiteSpace(c) || c == 0x200b; 1108 } 1109 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 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 1139 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec) 1140 { 1141 JSValue thisValue = exec->hostThisValue(); 1142 return JSValue::encode(trimString(exec, thisValue, TrimLeft)); 1143 } 1144 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