1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 (function(global, utils) { 6 7 %CheckIsBootstrapping(); 8 9 // ------------------------------------------------------------------- 10 // Imports 11 12 var ArrayIndexOf; 13 var ArrayJoin; 14 var GlobalRegExp = global.RegExp; 15 var GlobalString = global.String; 16 var IsRegExp; 17 var MakeRangeError; 18 var MakeTypeError; 19 var MaxSimple; 20 var MinSimple; 21 var RegExpInitialize; 22 var matchSymbol = utils.ImportNow("match_symbol"); 23 var replaceSymbol = utils.ImportNow("replace_symbol"); 24 var searchSymbol = utils.ImportNow("search_symbol"); 25 var splitSymbol = utils.ImportNow("split_symbol"); 26 27 utils.Import(function(from) { 28 ArrayIndexOf = from.ArrayIndexOf; 29 ArrayJoin = from.ArrayJoin; 30 IsRegExp = from.IsRegExp; 31 MakeRangeError = from.MakeRangeError; 32 MakeTypeError = from.MakeTypeError; 33 MaxSimple = from.MaxSimple; 34 MinSimple = from.MinSimple; 35 RegExpInitialize = from.RegExpInitialize; 36 }); 37 38 //------------------------------------------------------------------- 39 40 // ECMA-262 section 15.5.4.2 41 function StringToString() { 42 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { 43 throw MakeTypeError(kNotGeneric, 'String.prototype.toString'); 44 } 45 return %_ValueOf(this); 46 } 47 48 49 // ECMA-262 section 15.5.4.3 50 function StringValueOf() { 51 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { 52 throw MakeTypeError(kNotGeneric, 'String.prototype.valueOf'); 53 } 54 return %_ValueOf(this); 55 } 56 57 58 // ECMA-262, section 15.5.4.6 59 function StringConcat(other /* and more */) { // length == 1 60 "use strict"; 61 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat"); 62 var s = TO_STRING(this); 63 var len = arguments.length; 64 for (var i = 0; i < len; ++i) { 65 s = s + TO_STRING(arguments[i]); 66 } 67 return s; 68 } 69 70 71 // ECMA-262 section 15.5.4.7 72 function StringIndexOf(pattern, position) { // length == 1 73 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf"); 74 75 var subject = TO_STRING(this); 76 pattern = TO_STRING(pattern); 77 var index = TO_INTEGER(position); 78 if (index < 0) index = 0; 79 if (index > subject.length) index = subject.length; 80 return %StringIndexOf(subject, pattern, index); 81 } 82 83 %FunctionSetLength(StringIndexOf, 1); 84 85 86 // ECMA-262 section 15.5.4.8 87 function StringLastIndexOf(pat, pos) { // length == 1 88 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf"); 89 90 var sub = TO_STRING(this); 91 var subLength = sub.length; 92 var pat = TO_STRING(pat); 93 var patLength = pat.length; 94 var index = subLength - patLength; 95 var position = TO_NUMBER(pos); 96 if (!NUMBER_IS_NAN(position)) { 97 position = TO_INTEGER(position); 98 if (position < 0) { 99 position = 0; 100 } 101 if (position + patLength < subLength) { 102 index = position; 103 } 104 } 105 if (index < 0) { 106 return -1; 107 } 108 return %StringLastIndexOf(sub, pat, index); 109 } 110 111 %FunctionSetLength(StringLastIndexOf, 1); 112 113 114 // ECMA-262 section 15.5.4.9 115 // 116 // This function is implementation specific. For now, we do not 117 // do anything locale specific. 118 function StringLocaleCompareJS(other) { 119 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare"); 120 121 return %StringLocaleCompare(TO_STRING(this), TO_STRING(other)); 122 } 123 124 125 // ES6 21.1.3.11. 126 function StringMatchJS(pattern) { 127 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match"); 128 129 if (!IS_NULL_OR_UNDEFINED(pattern)) { 130 var matcher = pattern[matchSymbol]; 131 if (!IS_UNDEFINED(matcher)) { 132 return %_Call(matcher, pattern, this); 133 } 134 } 135 136 var subject = TO_STRING(this); 137 138 // Equivalent to RegExpCreate (ES#sec-regexpcreate) 139 var regexp = %_NewObject(GlobalRegExp, GlobalRegExp); 140 RegExpInitialize(regexp, pattern); 141 return regexp[matchSymbol](subject); 142 } 143 144 145 // ECMA-262 v6, section 21.1.3.12 146 // 147 // For now we do nothing, as proper normalization requires big tables. 148 // If Intl is enabled, then i18n.js will override it and provide the the 149 // proper functionality. 150 function StringNormalize(formArg) { // length == 0 151 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); 152 var s = TO_STRING(this); 153 154 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg); 155 156 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; 157 var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form); 158 if (normalizationForm === -1) { 159 throw MakeRangeError(kNormalizationForm, 160 %_Call(ArrayJoin, NORMALIZATION_FORMS, ', ')); 161 } 162 163 return s; 164 } 165 166 %FunctionSetLength(StringNormalize, 0); 167 168 169 // This has the same size as the RegExpLastMatchInfo array, and can be used 170 // for functions that expect that structure to be returned. It is used when 171 // the needle is a string rather than a regexp. In this case we can't update 172 // lastMatchArray without erroneously affecting the properties on the global 173 // RegExp object. 174 var reusableMatchInfo = [2, "", "", -1, -1]; 175 176 177 // ES6, section 21.1.3.14 178 function StringReplace(search, replace) { 179 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); 180 181 // Decision tree for dispatch 182 // .. regexp search (in src/js/regexp.js, RegExpReplace) 183 // .... string replace 184 // ...... non-global search 185 // ........ empty string replace 186 // ........ non-empty string replace (with $-expansion) 187 // ...... global search 188 // ........ no need to circumvent last match info override 189 // ........ need to circument last match info override 190 // .... function replace 191 // ...... global search 192 // ...... non-global search 193 // .. string search 194 // .... special case that replaces with one single character 195 // ...... function replace 196 // ...... string replace (with $-expansion) 197 198 if (!IS_NULL_OR_UNDEFINED(search)) { 199 var replacer = search[replaceSymbol]; 200 if (!IS_UNDEFINED(replacer)) { 201 return %_Call(replacer, search, this, replace); 202 } 203 } 204 205 var subject = TO_STRING(this); 206 207 search = TO_STRING(search); 208 209 if (search.length == 1 && 210 subject.length > 0xFF && 211 IS_STRING(replace) && 212 %StringIndexOf(replace, '$', 0) < 0) { 213 // Searching by traversing a cons string tree and replace with cons of 214 // slices works only when the replaced string is a single character, being 215 // replaced by a simple string and only pays off for long strings. 216 return %StringReplaceOneCharWithString(subject, search, replace); 217 } 218 var start = %StringIndexOf(subject, search, 0); 219 if (start < 0) return subject; 220 var end = start + search.length; 221 222 var result = %_SubString(subject, 0, start); 223 224 // Compute the string to replace with. 225 if (IS_CALLABLE(replace)) { 226 result += replace(search, start, subject); 227 } else { 228 reusableMatchInfo[CAPTURE0] = start; 229 reusableMatchInfo[CAPTURE1] = end; 230 result = ExpandReplacement(TO_STRING(replace), 231 subject, 232 reusableMatchInfo, 233 result); 234 } 235 236 return result + %_SubString(subject, end, subject.length); 237 } 238 239 240 // Expand the $-expressions in the string and return a new string with 241 // the result. 242 function ExpandReplacement(string, subject, matchInfo, result) { 243 var length = string.length; 244 var next = %StringIndexOf(string, '$', 0); 245 if (next < 0) { 246 if (length > 0) result += string; 247 return result; 248 } 249 250 if (next > 0) result += %_SubString(string, 0, next); 251 252 while (true) { 253 var expansion = '$'; 254 var position = next + 1; 255 if (position < length) { 256 var peek = %_StringCharCodeAt(string, position); 257 if (peek == 36) { // $$ 258 ++position; 259 result += '$'; 260 } else if (peek == 38) { // $& - match 261 ++position; 262 result += 263 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); 264 } else if (peek == 96) { // $` - prefix 265 ++position; 266 result += %_SubString(subject, 0, matchInfo[CAPTURE0]); 267 } else if (peek == 39) { // $' - suffix 268 ++position; 269 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length); 270 } else if (peek >= 48 && peek <= 57) { 271 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 272 var scaled_index = (peek - 48) << 1; 273 var advance = 1; 274 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo); 275 if (position + 1 < string.length) { 276 var next = %_StringCharCodeAt(string, position + 1); 277 if (next >= 48 && next <= 57) { 278 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1); 279 if (new_scaled_index < number_of_captures) { 280 scaled_index = new_scaled_index; 281 advance = 2; 282 } 283 } 284 } 285 if (scaled_index != 0 && scaled_index < number_of_captures) { 286 var start = matchInfo[CAPTURE(scaled_index)]; 287 if (start >= 0) { 288 result += 289 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]); 290 } 291 position += advance; 292 } else { 293 result += '$'; 294 } 295 } else { 296 result += '$'; 297 } 298 } else { 299 result += '$'; 300 } 301 302 // Go the the next $ in the string. 303 next = %StringIndexOf(string, '$', position); 304 305 // Return if there are no more $ characters in the string. If we 306 // haven't reached the end, we need to append the suffix. 307 if (next < 0) { 308 if (position < length) { 309 result += %_SubString(string, position, length); 310 } 311 return result; 312 } 313 314 // Append substring between the previous and the next $ character. 315 if (next > position) { 316 result += %_SubString(string, position, next); 317 } 318 } 319 return result; 320 } 321 322 323 // ES6 21.1.3.15. 324 function StringSearch(pattern) { 325 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); 326 327 if (!IS_NULL_OR_UNDEFINED(pattern)) { 328 var searcher = pattern[searchSymbol]; 329 if (!IS_UNDEFINED(searcher)) { 330 return %_Call(searcher, pattern, this); 331 } 332 } 333 334 var subject = TO_STRING(this); 335 336 // Equivalent to RegExpCreate (ES#sec-regexpcreate) 337 var regexp = %_NewObject(GlobalRegExp, GlobalRegExp); 338 RegExpInitialize(regexp, pattern); 339 return %_Call(regexp[searchSymbol], regexp, subject); 340 } 341 342 343 // ECMA-262 section 15.5.4.13 344 function StringSlice(start, end) { 345 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice"); 346 347 var s = TO_STRING(this); 348 var s_len = s.length; 349 var start_i = TO_INTEGER(start); 350 var end_i = s_len; 351 if (!IS_UNDEFINED(end)) { 352 end_i = TO_INTEGER(end); 353 } 354 355 if (start_i < 0) { 356 start_i += s_len; 357 if (start_i < 0) { 358 start_i = 0; 359 } 360 } else { 361 if (start_i > s_len) { 362 return ''; 363 } 364 } 365 366 if (end_i < 0) { 367 end_i += s_len; 368 if (end_i < 0) { 369 return ''; 370 } 371 } else { 372 if (end_i > s_len) { 373 end_i = s_len; 374 } 375 } 376 377 if (end_i <= start_i) { 378 return ''; 379 } 380 381 return %_SubString(s, start_i, end_i); 382 } 383 384 385 // ES6 21.1.3.17. 386 function StringSplitJS(separator, limit) { 387 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split"); 388 389 if (!IS_NULL_OR_UNDEFINED(separator)) { 390 var splitter = separator[splitSymbol]; 391 if (!IS_UNDEFINED(splitter)) { 392 return %_Call(splitter, separator, this, limit); 393 } 394 } 395 396 var subject = TO_STRING(this); 397 limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit); 398 399 var length = subject.length; 400 var separator_string = TO_STRING(separator); 401 402 if (limit === 0) return []; 403 404 // ECMA-262 says that if separator is undefined, the result should 405 // be an array of size 1 containing the entire string. 406 if (IS_UNDEFINED(separator)) return [subject]; 407 408 var separator_length = separator_string.length; 409 410 // If the separator string is empty then return the elements in the subject. 411 if (separator_length === 0) return %StringToArray(subject, limit); 412 413 return %StringSplit(subject, separator_string, limit); 414 } 415 416 417 // ECMA-262 section 15.5.4.15 418 function StringSubstring(start, end) { 419 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString"); 420 421 var s = TO_STRING(this); 422 var s_len = s.length; 423 424 var start_i = TO_INTEGER(start); 425 if (start_i < 0) { 426 start_i = 0; 427 } else if (start_i > s_len) { 428 start_i = s_len; 429 } 430 431 var end_i = s_len; 432 if (!IS_UNDEFINED(end)) { 433 end_i = TO_INTEGER(end); 434 if (end_i > s_len) { 435 end_i = s_len; 436 } else { 437 if (end_i < 0) end_i = 0; 438 if (start_i > end_i) { 439 var tmp = end_i; 440 end_i = start_i; 441 start_i = tmp; 442 } 443 } 444 } 445 446 return %_SubString(s, start_i, end_i); 447 } 448 449 450 // ES6 draft, revision 26 (2014-07-18), section B.2.3.1 451 function StringSubstr(start, n) { 452 CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr"); 453 454 var s = TO_STRING(this); 455 var len; 456 457 // Correct n: If not given, set to string length; if explicitly 458 // set to undefined, zero, or negative, returns empty string. 459 if (IS_UNDEFINED(n)) { 460 len = s.length; 461 } else { 462 len = TO_INTEGER(n); 463 if (len <= 0) return ''; 464 } 465 466 // Correct start: If not given (or undefined), set to zero; otherwise 467 // convert to integer and handle negative case. 468 if (IS_UNDEFINED(start)) { 469 start = 0; 470 } else { 471 start = TO_INTEGER(start); 472 // If positive, and greater than or equal to the string length, 473 // return empty string. 474 if (start >= s.length) return ''; 475 // If negative and absolute value is larger than the string length, 476 // use zero. 477 if (start < 0) { 478 start += s.length; 479 if (start < 0) start = 0; 480 } 481 } 482 483 var end = start + len; 484 if (end > s.length) end = s.length; 485 486 return %_SubString(s, start, end); 487 } 488 489 490 // ECMA-262, 15.5.4.16 491 function StringToLowerCaseJS() { 492 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase"); 493 494 return %StringToLowerCase(TO_STRING(this)); 495 } 496 497 498 // ECMA-262, 15.5.4.17 499 function StringToLocaleLowerCase() { 500 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase"); 501 502 return %StringToLowerCase(TO_STRING(this)); 503 } 504 505 506 // ECMA-262, 15.5.4.18 507 function StringToUpperCaseJS() { 508 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase"); 509 510 return %StringToUpperCase(TO_STRING(this)); 511 } 512 513 514 // ECMA-262, 15.5.4.19 515 function StringToLocaleUpperCase() { 516 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase"); 517 518 return %StringToUpperCase(TO_STRING(this)); 519 } 520 521 522 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1 523 function HtmlEscape(str) { 524 return %_Call(StringReplace, TO_STRING(str), /"/g, """); 525 } 526 527 528 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2 529 function StringAnchor(name) { 530 CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor"); 531 return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) + 532 "</a>"; 533 } 534 535 536 // ES6 draft, revision 26 (2014-07-18), section B.2.3.3 537 function StringBig() { 538 CHECK_OBJECT_COERCIBLE(this, "String.prototype.big"); 539 return "<big>" + TO_STRING(this) + "</big>"; 540 } 541 542 543 // ES6 draft, revision 26 (2014-07-18), section B.2.3.4 544 function StringBlink() { 545 CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink"); 546 return "<blink>" + TO_STRING(this) + "</blink>"; 547 } 548 549 550 // ES6 draft, revision 26 (2014-07-18), section B.2.3.5 551 function StringBold() { 552 CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold"); 553 return "<b>" + TO_STRING(this) + "</b>"; 554 } 555 556 557 // ES6 draft, revision 26 (2014-07-18), section B.2.3.6 558 function StringFixed() { 559 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed"); 560 return "<tt>" + TO_STRING(this) + "</tt>"; 561 } 562 563 564 // ES6 draft, revision 26 (2014-07-18), section B.2.3.7 565 function StringFontcolor(color) { 566 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor"); 567 return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) + 568 "</font>"; 569 } 570 571 572 // ES6 draft, revision 26 (2014-07-18), section B.2.3.8 573 function StringFontsize(size) { 574 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize"); 575 return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) + 576 "</font>"; 577 } 578 579 580 // ES6 draft, revision 26 (2014-07-18), section B.2.3.9 581 function StringItalics() { 582 CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics"); 583 return "<i>" + TO_STRING(this) + "</i>"; 584 } 585 586 587 // ES6 draft, revision 26 (2014-07-18), section B.2.3.10 588 function StringLink(s) { 589 CHECK_OBJECT_COERCIBLE(this, "String.prototype.link"); 590 return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>"; 591 } 592 593 594 // ES6 draft, revision 26 (2014-07-18), section B.2.3.11 595 function StringSmall() { 596 CHECK_OBJECT_COERCIBLE(this, "String.prototype.small"); 597 return "<small>" + TO_STRING(this) + "</small>"; 598 } 599 600 601 // ES6 draft, revision 26 (2014-07-18), section B.2.3.12 602 function StringStrike() { 603 CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike"); 604 return "<strike>" + TO_STRING(this) + "</strike>"; 605 } 606 607 608 // ES6 draft, revision 26 (2014-07-18), section B.2.3.13 609 function StringSub() { 610 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub"); 611 return "<sub>" + TO_STRING(this) + "</sub>"; 612 } 613 614 615 // ES6 draft, revision 26 (2014-07-18), section B.2.3.14 616 function StringSup() { 617 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup"); 618 return "<sup>" + TO_STRING(this) + "</sup>"; 619 } 620 621 // ES6, section 21.1.3.13 622 function StringRepeat(count) { 623 CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat"); 624 625 var s = TO_STRING(this); 626 var n = TO_INTEGER(count); 627 628 if (n < 0 || n === INFINITY) throw MakeRangeError(kInvalidCountValue); 629 630 // Early return to allow an arbitrarily-large repeat of the empty string. 631 if (s.length === 0) return ""; 632 633 // The maximum string length is stored in a smi, so a longer repeat 634 // must result in a range error. 635 if (n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue); 636 637 var r = ""; 638 while (true) { 639 if (n & 1) r += s; 640 n >>= 1; 641 if (n === 0) return r; 642 s += s; 643 } 644 } 645 646 647 // ES6 draft 04-05-14, section 21.1.3.18 648 function StringStartsWith(searchString, position) { // length == 1 649 CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith"); 650 651 var s = TO_STRING(this); 652 653 if (IsRegExp(searchString)) { 654 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith"); 655 } 656 657 var ss = TO_STRING(searchString); 658 var pos = TO_INTEGER(position); 659 660 var s_len = s.length; 661 var start = MinSimple(MaxSimple(pos, 0), s_len); 662 var ss_len = ss.length; 663 if (ss_len + start > s_len) { 664 return false; 665 } 666 667 return %_SubString(s, start, start + ss_len) === ss; 668 } 669 670 %FunctionSetLength(StringStartsWith, 1); 671 672 673 // ES6 draft 04-05-14, section 21.1.3.7 674 function StringEndsWith(searchString, position) { // length == 1 675 CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith"); 676 677 var s = TO_STRING(this); 678 679 if (IsRegExp(searchString)) { 680 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith"); 681 } 682 683 var ss = TO_STRING(searchString); 684 var s_len = s.length; 685 var pos = !IS_UNDEFINED(position) ? TO_INTEGER(position) : s_len 686 687 var end = MinSimple(MaxSimple(pos, 0), s_len); 688 var ss_len = ss.length; 689 var start = end - ss_len; 690 if (start < 0) { 691 return false; 692 } 693 694 return %_SubString(s, start, start + ss_len) === ss; 695 } 696 697 %FunctionSetLength(StringEndsWith, 1); 698 699 700 // ES6 draft 04-05-14, section 21.1.3.6 701 function StringIncludes(searchString, position) { // length == 1 702 CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes"); 703 704 var string = TO_STRING(this); 705 706 if (IsRegExp(searchString)) { 707 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes"); 708 } 709 710 searchString = TO_STRING(searchString); 711 var pos = TO_INTEGER(position); 712 713 var stringLength = string.length; 714 if (pos < 0) pos = 0; 715 if (pos > stringLength) pos = stringLength; 716 var searchStringLength = searchString.length; 717 718 if (searchStringLength + pos > stringLength) { 719 return false; 720 } 721 722 return %StringIndexOf(string, searchString, pos) !== -1; 723 } 724 725 %FunctionSetLength(StringIncludes, 1); 726 727 728 // ES6 Draft 05-22-2014, section 21.1.3.3 729 function StringCodePointAt(pos) { 730 CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt"); 731 732 var string = TO_STRING(this); 733 var size = string.length; 734 pos = TO_INTEGER(pos); 735 if (pos < 0 || pos >= size) { 736 return UNDEFINED; 737 } 738 var first = %_StringCharCodeAt(string, pos); 739 if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) { 740 return first; 741 } 742 var second = %_StringCharCodeAt(string, pos + 1); 743 if (second < 0xDC00 || second > 0xDFFF) { 744 return first; 745 } 746 return (first - 0xD800) * 0x400 + second + 0x2400; 747 } 748 749 750 // ------------------------------------------------------------------- 751 // String methods related to templates 752 753 // ES6 Draft 03-17-2015, section 21.1.2.4 754 function StringRaw(callSite) { 755 "use strict"; 756 var numberOfSubstitutions = arguments.length; 757 var cooked = TO_OBJECT(callSite); 758 var raw = TO_OBJECT(cooked.raw); 759 var literalSegments = TO_LENGTH(raw.length); 760 if (literalSegments <= 0) return ""; 761 762 var result = TO_STRING(raw[0]); 763 764 for (var i = 1; i < literalSegments; ++i) { 765 if (i < numberOfSubstitutions) { 766 result += TO_STRING(arguments[i]); 767 } 768 result += TO_STRING(raw[i]); 769 } 770 771 return result; 772 } 773 774 // ------------------------------------------------------------------- 775 776 // Set up the non-enumerable functions on the String object. 777 utils.InstallFunctions(GlobalString, DONT_ENUM, [ 778 "raw", StringRaw 779 ]); 780 781 // Set up the non-enumerable functions on the String prototype object. 782 utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ 783 "valueOf", StringValueOf, 784 "toString", StringToString, 785 "codePointAt", StringCodePointAt, 786 "concat", StringConcat, 787 "endsWith", StringEndsWith, 788 "includes", StringIncludes, 789 "indexOf", StringIndexOf, 790 "lastIndexOf", StringLastIndexOf, 791 "localeCompare", StringLocaleCompareJS, 792 "match", StringMatchJS, 793 "normalize", StringNormalize, 794 "repeat", StringRepeat, 795 "replace", StringReplace, 796 "search", StringSearch, 797 "slice", StringSlice, 798 "split", StringSplitJS, 799 "substring", StringSubstring, 800 "substr", StringSubstr, 801 "startsWith", StringStartsWith, 802 "toLowerCase", StringToLowerCaseJS, 803 "toLocaleLowerCase", StringToLocaleLowerCase, 804 "toUpperCase", StringToUpperCaseJS, 805 "toLocaleUpperCase", StringToLocaleUpperCase, 806 807 "link", StringLink, 808 "anchor", StringAnchor, 809 "fontcolor", StringFontcolor, 810 "fontsize", StringFontsize, 811 "big", StringBig, 812 "blink", StringBlink, 813 "bold", StringBold, 814 "fixed", StringFixed, 815 "italics", StringItalics, 816 "small", StringSmall, 817 "strike", StringStrike, 818 "sub", StringSub, 819 "sup", StringSup 820 ]); 821 822 // ------------------------------------------------------------------- 823 // Exports 824 825 utils.Export(function(to) { 826 to.ExpandReplacement = ExpandReplacement; 827 to.StringIndexOf = StringIndexOf; 828 to.StringLastIndexOf = StringLastIndexOf; 829 to.StringMatch = StringMatchJS; 830 to.StringReplace = StringReplace; 831 to.StringSlice = StringSlice; 832 to.StringSplit = StringSplitJS; 833 to.StringSubstr = StringSubstr; 834 to.StringSubstring = StringSubstring; 835 }); 836 837 }) 838