Home | History | Annotate | Download | only in js
      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, "&quot;");
    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