No bug - tagging b4d3227540c9ebc43d64aac6168fdca7019c22d8 with FIREFOX_BETA_126_BASE...
[gecko.git] / js / src / builtin / String.js
blob864c6ad588ec2b8f4d2afa55fa9bfdb0a249f969
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 function StringProtoHasNoMatch() {
6   var ObjectProto = GetBuiltinPrototype("Object");
7   var StringProto = GetBuiltinPrototype("String");
8   if (!ObjectHasPrototype(StringProto, ObjectProto)) {
9     return false;
10   }
11   return !(GetBuiltinSymbol("match") in StringProto);
14 function IsStringMatchOptimizable() {
15   var RegExpProto = GetBuiltinPrototype("RegExp");
16   // If RegExpPrototypeOptimizable succeeds, `exec` and `@@match` are
17   // guaranteed to be data properties.
18   return (
19     RegExpPrototypeOptimizable(RegExpProto) &&
20     RegExpProto.exec === RegExp_prototype_Exec &&
21     RegExpProto[GetBuiltinSymbol("match")] === RegExpMatch
22   );
25 function ThrowIncompatibleMethod(name, thisv) {
26   ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "String", name, ToString(thisv));
29 // ES 2016 draft Mar 25, 2016 21.1.3.11.
30 function String_match(regexp) {
31   // Step 1.
32   if (IsNullOrUndefined(this)) {
33     ThrowIncompatibleMethod("match", this);
34   }
36   // Step 2.
37   var isPatternString = typeof regexp === "string";
38   if (
39     !(isPatternString && StringProtoHasNoMatch()) &&
40     !IsNullOrUndefined(regexp)
41   ) {
42     // Step 2.a.
43     var matcher = GetMethod(regexp, GetBuiltinSymbol("match"));
45     // Step 2.b.
46     if (matcher !== undefined) {
47       return callContentFunction(matcher, regexp, this);
48     }
49   }
51   // Step 3.
52   var S = ToString(this);
54   if (isPatternString && IsStringMatchOptimizable()) {
55     var flatResult = FlatStringMatch(S, regexp);
56     if (flatResult !== undefined) {
57       return flatResult;
58     }
59   }
61   // Step 4.
62   var rx = RegExpCreate(regexp);
64   // Step 5 (optimized case).
65   if (IsStringMatchOptimizable()) {
66     return RegExpMatcher(rx, S, 0);
67   }
69   // Step 5.
70   return callContentFunction(GetMethod(rx, GetBuiltinSymbol("match")), rx, S);
73 // String.prototype.matchAll proposal.
75 // String.prototype.matchAll ( regexp )
76 function String_matchAll(regexp) {
77   // Step 1.
78   if (IsNullOrUndefined(this)) {
79     ThrowIncompatibleMethod("matchAll", this);
80   }
82   // Step 2.
83   if (!IsNullOrUndefined(regexp)) {
84     // Steps 2.a-b.
85     if (IsRegExp(regexp)) {
86       // Step 2.b.i.
87       var flags = regexp.flags;
89       // Step 2.b.ii.
90       if (IsNullOrUndefined(flags)) {
91         ThrowTypeError(JSMSG_FLAGS_UNDEFINED_OR_NULL);
92       }
94       // Step 2.b.iii.
95       if (!callFunction(std_String_includes, ToString(flags), "g")) {
96         ThrowTypeError(JSMSG_REQUIRES_GLOBAL_REGEXP, "matchAll");
97       }
98     }
100     // Step 2.c.
101     var matcher = GetMethod(regexp, GetBuiltinSymbol("matchAll"));
103     // Step 2.d.
104     if (matcher !== undefined) {
105       return callContentFunction(matcher, regexp, this);
106     }
107   }
109   // Step 3.
110   var string = ToString(this);
112   // Step 4.
113   var rx = RegExpCreate(regexp, "g");
115   // Step 5.
116   return callContentFunction(
117     GetMethod(rx, GetBuiltinSymbol("matchAll")),
118     rx,
119     string
120   );
124  * A helper function implementing the logic for both String.prototype.padStart
125  * and String.prototype.padEnd as described in ES7 Draft March 29, 2016
126  */
127 function String_pad(maxLength, fillString, padEnd) {
128   // Step 1.
129   if (IsNullOrUndefined(this)) {
130     ThrowIncompatibleMethod(padEnd ? "padEnd" : "padStart", this);
131   }
133   // Step 2.
134   var str = ToString(this);
136   // Steps 3-4.
137   var intMaxLength = ToLength(maxLength);
138   var strLen = str.length;
140   // Step 5.
141   if (intMaxLength <= strLen) {
142     return str;
143   }
145   // Steps 6-7.
146   assert(fillString !== undefined, "never called when fillString is undefined");
147   var filler = ToString(fillString);
149   // Step 8.
150   if (filler === "") {
151     return str;
152   }
154   // Throw an error if the final string length exceeds the maximum string
155   // length. Perform this check early so we can use int32 operations below.
156   if (intMaxLength > MAX_STRING_LENGTH) {
157     ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE);
158   }
160   // Step 9.
161   var fillLen = intMaxLength - strLen;
163   // Step 10.
164   // Perform an int32 division to ensure String_repeat is not called with a
165   // double to avoid repeated bailouts in ToInteger.
166   var truncatedStringFiller = callFunction(
167     String_repeat,
168     filler,
169     (fillLen / filler.length) | 0
170   );
172   truncatedStringFiller += Substring(filler, 0, fillLen % filler.length);
174   // Step 11.
175   if (padEnd === true) {
176     return str + truncatedStringFiller;
177   }
178   return truncatedStringFiller + str;
181 function String_pad_start(maxLength, fillString = " ") {
182   return callFunction(String_pad, this, maxLength, fillString, false);
185 function String_pad_end(maxLength, fillString = " ") {
186   return callFunction(String_pad, this, maxLength, fillString, true);
189 function StringProtoHasNoReplace() {
190   var ObjectProto = GetBuiltinPrototype("Object");
191   var StringProto = GetBuiltinPrototype("String");
192   if (!ObjectHasPrototype(StringProto, ObjectProto)) {
193     return false;
194   }
195   return !(GetBuiltinSymbol("replace") in StringProto);
198 // A thin wrapper to call SubstringKernel with int32-typed arguments.
199 // Caller should check the range of |from| and |length|.
200 function Substring(str, from, length) {
201   assert(typeof str === "string", "|str| should be a string");
202   assert(
203     (from | 0) === from,
204     "coercing |from| into int32 should not change the value"
205   );
206   assert(
207     (length | 0) === length,
208     "coercing |length| into int32 should not change the value"
209   );
211   return SubstringKernel(
212     str,
213     std_Math_max(from, 0) | 0,
214     std_Math_max(length, 0) | 0
215   );
218 // ES 2016 draft Mar 25, 2016 21.1.3.14.
219 function String_replace(searchValue, replaceValue) {
220   // Step 1.
221   if (IsNullOrUndefined(this)) {
222     ThrowIncompatibleMethod("replace", this);
223   }
225   // Step 2.
226   if (
227     !(typeof searchValue === "string" && StringProtoHasNoReplace()) &&
228     !IsNullOrUndefined(searchValue)
229   ) {
230     // Step 2.a.
231     var replacer = GetMethod(searchValue, GetBuiltinSymbol("replace"));
233     // Step 2.b.
234     if (replacer !== undefined) {
235       return callContentFunction(replacer, searchValue, this, replaceValue);
236     }
237   }
239   // Step 3.
240   var string = ToString(this);
242   // Step 4.
243   var searchString = ToString(searchValue);
245   if (typeof replaceValue === "string") {
246     // Steps 6-12: Optimized for string case.
247     return StringReplaceString(string, searchString, replaceValue);
248   }
250   // Step 5.
251   if (!IsCallable(replaceValue)) {
252     // Steps 6-12.
253     return StringReplaceString(string, searchString, ToString(replaceValue));
254   }
256   // Step 7.
257   var pos = callFunction(std_String_indexOf, string, searchString);
258   if (pos === -1) {
259     return string;
260   }
262   // Step 8.
263   var replStr = ToString(
264     callContentFunction(replaceValue, undefined, searchString, pos, string)
265   );
267   // Step 10.
268   var tailPos = pos + searchString.length;
270   // Step 11.
271   var newString;
272   if (pos === 0) {
273     newString = "";
274   } else {
275     newString = Substring(string, 0, pos);
276   }
278   newString += replStr;
279   var stringLength = string.length;
280   if (tailPos < stringLength) {
281     newString += Substring(string, tailPos, stringLength - tailPos);
282   }
284   // Step 12.
285   return newString;
288 // String.prototype.replaceAll (Stage 3 proposal)
289 // https://tc39.es/proposal-string-replaceall/
291 // String.prototype.replaceAll ( searchValue, replaceValue )
292 function String_replaceAll(searchValue, replaceValue) {
293   // Step 1.
294   if (IsNullOrUndefined(this)) {
295     ThrowIncompatibleMethod("replaceAll", this);
296   }
298   // Step 2.
299   if (!IsNullOrUndefined(searchValue)) {
300     // Steps 2.a-b.
301     if (IsRegExp(searchValue)) {
302       // Step 2.b.i.
303       var flags = searchValue.flags;
305       // Step 2.b.ii.
306       if (IsNullOrUndefined(flags)) {
307         ThrowTypeError(JSMSG_FLAGS_UNDEFINED_OR_NULL);
308       }
310       // Step 2.b.iii.
311       if (!callFunction(std_String_includes, ToString(flags), "g")) {
312         ThrowTypeError(JSMSG_REQUIRES_GLOBAL_REGEXP, "replaceAll");
313       }
314     }
316     // Step 2.c.
317     var replacer = GetMethod(searchValue, GetBuiltinSymbol("replace"));
319     // Step 2.b.
320     if (replacer !== undefined) {
321       return callContentFunction(replacer, searchValue, this, replaceValue);
322     }
323   }
325   // Step 3.
326   var string = ToString(this);
328   // Step 4.
329   var searchString = ToString(searchValue);
331   // Steps 5-6.
332   if (!IsCallable(replaceValue)) {
333     // Steps 7-16.
334     return StringReplaceAllString(string, searchString, ToString(replaceValue));
335   }
337   // Step 7.
338   var searchLength = searchString.length;
340   // Step 8.
341   var advanceBy = std_Math_max(1, searchLength);
343   // Step 9 (not needed in this implementation).
345   // Step 12.
346   var endOfLastMatch = 0;
348   // Step 13.
349   var result = "";
351   // Steps 10-11, 14.
352   var position = 0;
353   while (true) {
354     // Steps 10-11.
355     //
356     // StringIndexOf doesn't clamp the |position| argument to the input
357     // string length, i.e. |StringIndexOf("abc", "", 4)| returns -1,
358     // whereas |"abc".indexOf("", 4)| returns 3. That means we need to
359     // exit the loop when |nextPosition| is smaller than |position| and
360     // not just when |nextPosition| is -1.
361     var nextPosition = callFunction(
362       std_String_indexOf,
363       string,
364       searchString,
365       position
366     );
367     if (nextPosition < position) {
368       break;
369     }
370     position = nextPosition;
372     // Step 14.a.
373     var replacement = ToString(
374       callContentFunction(
375         replaceValue,
376         undefined,
377         searchString,
378         position,
379         string
380       )
381     );
383     // Step 14.b (not applicable).
385     // Step 14.c.
386     var stringSlice = Substring(
387       string,
388       endOfLastMatch,
389       position - endOfLastMatch
390     );
392     // Step 14.d.
393     result += stringSlice + replacement;
395     // Step 14.e.
396     endOfLastMatch = position + searchLength;
398     // Step 11.b.
399     position += advanceBy;
400   }
402   // Step 15.
403   if (endOfLastMatch < string.length) {
404     // Step 15.a.
405     result += Substring(string, endOfLastMatch, string.length - endOfLastMatch);
406   }
408   // Step 16.
409   return result;
412 function StringProtoHasNoSearch() {
413   var ObjectProto = GetBuiltinPrototype("Object");
414   var StringProto = GetBuiltinPrototype("String");
415   if (!ObjectHasPrototype(StringProto, ObjectProto)) {
416     return false;
417   }
418   return !(GetBuiltinSymbol("search") in StringProto);
421 function IsStringSearchOptimizable() {
422   var RegExpProto = GetBuiltinPrototype("RegExp");
423   // If RegExpPrototypeOptimizable succeeds, `exec` and `@@search` are
424   // guaranteed to be data properties.
425   return (
426     RegExpPrototypeOptimizable(RegExpProto) &&
427     RegExpProto.exec === RegExp_prototype_Exec &&
428     RegExpProto[GetBuiltinSymbol("search")] === RegExpSearch
429   );
432 // ES 2016 draft Mar 25, 2016 21.1.3.15.
433 function String_search(regexp) {
434   // Step 1.
435   if (IsNullOrUndefined(this)) {
436     ThrowIncompatibleMethod("search", this);
437   }
439   // Step 2.
440   var isPatternString = typeof regexp === "string";
441   if (
442     !(isPatternString && StringProtoHasNoSearch()) &&
443     !IsNullOrUndefined(regexp)
444   ) {
445     // Step 2.a.
446     var searcher = GetMethod(regexp, GetBuiltinSymbol("search"));
448     // Step 2.b.
449     if (searcher !== undefined) {
450       return callContentFunction(searcher, regexp, this);
451     }
452   }
454   // Step 3.
455   var string = ToString(this);
457   if (isPatternString && IsStringSearchOptimizable()) {
458     var flatResult = FlatStringSearch(string, regexp);
459     if (flatResult !== -2) {
460       return flatResult;
461     }
462   }
464   // Step 4.
465   var rx = RegExpCreate(regexp);
467   // Step 5.
468   return callContentFunction(
469     GetMethod(rx, GetBuiltinSymbol("search")),
470     rx,
471     string
472   );
475 function StringProtoHasNoSplit() {
476   var ObjectProto = GetBuiltinPrototype("Object");
477   var StringProto = GetBuiltinPrototype("String");
478   if (!ObjectHasPrototype(StringProto, ObjectProto)) {
479     return false;
480   }
481   return !(GetBuiltinSymbol("split") in StringProto);
484 // ES 2016 draft Mar 25, 2016 21.1.3.17.
485 function String_split(separator, limit) {
486   // Step 1.
487   if (IsNullOrUndefined(this)) {
488     ThrowIncompatibleMethod("split", this);
489   }
491   // Optimized path for string.split(string), especially when both strings
492   // are constants.  Following sequence of if's cannot be put together in
493   // order that IonMonkey sees the constant if present (bug 1246141).
494   if (typeof this === "string") {
495     if (StringProtoHasNoSplit()) {
496       if (typeof separator === "string") {
497         if (limit === undefined) {
498           // inlineConstantStringSplitString needs both arguments to
499           // be MConstant, so pass them directly.
500           return StringSplitString(this, separator);
501         }
502       }
503     }
504   }
506   // Step 2.
507   if (
508     !(typeof separator === "string" && StringProtoHasNoSplit()) &&
509     !IsNullOrUndefined(separator)
510   ) {
511     // Step 2.a.
512     var splitter = GetMethod(separator, GetBuiltinSymbol("split"));
514     // Step 2.b.
515     if (splitter !== undefined) {
516       return callContentFunction(splitter, separator, this, limit);
517     }
518   }
520   // Step 3.
521   var S = ToString(this);
523   // Step 6.
524   var R;
525   if (limit !== undefined) {
526     var lim = limit >>> 0;
528     // Step 9.
529     R = ToString(separator);
531     // Step 10.
532     if (lim === 0) {
533       return [];
534     }
536     // Step 11.
537     if (separator === undefined) {
538       return [S];
539     }
541     // Steps 4, 8, 12-18.
542     return StringSplitStringLimit(S, R, lim);
543   }
545   // Step 9.
546   R = ToString(separator);
548   // Step 11.
549   if (separator === undefined) {
550     return [S];
551   }
553   // Optimized path.
554   // Steps 4, 8, 12-18.
555   return StringSplitString(S, R);
558 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
559 // 21.1.3.22 String.prototype.substring ( start, end )
560 function String_substring(start, end) {
561   // Step 1.
562   if (IsNullOrUndefined(this)) {
563     ThrowIncompatibleMethod("substring", this);
564   }
566   // Step 2.
567   var str = ToString(this);
569   // Step 3.
570   var len = str.length;
572   // Step 4.
573   var intStart = ToInteger(start);
575   // Step 5.
576   var intEnd = end === undefined ? len : ToInteger(end);
578   // Step 6.
579   var finalStart = std_Math_min(std_Math_max(intStart, 0), len);
581   // Step 7.
582   var finalEnd = std_Math_min(std_Math_max(intEnd, 0), len);
584   // Step 8.
585   var from = std_Math_min(finalStart, finalEnd);
587   // Step 9.
588   var to = std_Math_max(finalStart, finalEnd);
590   // Step 10.
591   // While |from| and |to - from| are bounded to the length of |str| and this
592   // and thus definitely in the int32 range, they can still be typed as
593   // double. Eagerly truncate since SubstringKernel only accepts int32.
594   return SubstringKernel(str, from | 0, (to - from) | 0);
596 SetIsInlinableLargeFunction(String_substring);
598 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
599 // B.2.3.1 String.prototype.substr ( start, length )
600 function String_substr(start, length) {
601   // Steps 1.
602   if (IsNullOrUndefined(this)) {
603     ThrowIncompatibleMethod("substr", this);
604   }
606   // Step 2.
607   var str = ToString(this);
609   // Step 3.
610   var intStart = ToInteger(start);
612   // Steps 4-5.
613   var size = str.length;
614   // Use |size| instead of +Infinity to avoid performing calculations with
615   // doubles. (The result is the same either way.)
616   var end = length === undefined ? size : ToInteger(length);
618   // Step 6.
619   if (intStart < 0) {
620     intStart = std_Math_max(intStart + size, 0);
621   } else {
622     // Restrict the input range to allow better Ion optimizations.
623     intStart = std_Math_min(intStart, size);
624   }
626   // Step 7.
627   var resultLength = std_Math_min(std_Math_max(end, 0), size - intStart);
629   // Step 8.
630   assert(
631     0 <= resultLength && resultLength <= size - intStart,
632     "resultLength is a valid substring length value"
633   );
635   // Step 9.
636   // While |intStart| and |resultLength| are bounded to the length of |str|
637   // and thus definitely in the int32 range, they can still be typed as
638   // double. Eagerly truncate since SubstringKernel only accepts int32.
639   return SubstringKernel(str, intStart | 0, resultLength | 0);
641 SetIsInlinableLargeFunction(String_substr);
643 // ES2021 draft rev 12a546b92275a0e2f834017db2727bb9c6f6c8fd
644 // 21.1.3.4 String.prototype.concat ( ...args )
645 // Note: String.prototype.concat.length is 1.
646 function String_concat(arg1) {
647   // Step 1.
648   if (IsNullOrUndefined(this)) {
649     ThrowIncompatibleMethod("concat", this);
650   }
652   // Step 2.
653   var str = ToString(this);
655   // Specialize for the most common number of arguments for better inlining.
656   if (ArgumentsLength() === 0) {
657     return str;
658   }
659   if (ArgumentsLength() === 1) {
660     return str + ToString(GetArgument(0));
661   }
662   if (ArgumentsLength() === 2) {
663     return str + ToString(GetArgument(0)) + ToString(GetArgument(1));
664   }
666   // Step 3. (implicit)
667   // Step 4.
668   var result = str;
670   // Step 5.
671   for (var i = 0; i < ArgumentsLength(); i++) {
672     // Steps 5.a-b.
673     var nextString = ToString(GetArgument(i));
674     // Step 5.c.
675     result += nextString;
676   }
678   // Step 6.
679   return result;
682 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
683 // 21.1.3.19 String.prototype.slice ( start, end )
684 function String_slice(start, end) {
685   // Step 1.
686   if (IsNullOrUndefined(this)) {
687     ThrowIncompatibleMethod("slice", this);
688   }
690   // Step 2.
691   var str = ToString(this);
693   // Step 3.
694   var len = str.length;
696   // Step 4.
697   var intStart = ToInteger(start);
699   // Step 5.
700   var intEnd = end === undefined ? len : ToInteger(end);
702   // Step 6.
703   var from =
704     intStart < 0
705       ? std_Math_max(len + intStart, 0)
706       : std_Math_min(intStart, len);
708   // Step 7.
709   var to =
710     intEnd < 0 ? std_Math_max(len + intEnd, 0) : std_Math_min(intEnd, len);
712   // Step 8.
713   var span = std_Math_max(to - from, 0);
715   // Step 9.
716   // While |from| and |span| are bounded to the length of |str|
717   // and thus definitely in the int32 range, they can still be typed as
718   // double. Eagerly truncate since SubstringKernel only accepts int32.
719   return SubstringKernel(str, from | 0, span | 0);
721 SetIsInlinableLargeFunction(String_slice);
723 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
724 // 21.1.3.16 String.prototype.repeat ( count )
725 function String_repeat(count) {
726   // Step 1.
727   if (IsNullOrUndefined(this)) {
728     ThrowIncompatibleMethod("repeat", this);
729   }
731   // Step 2.
732   var S = ToString(this);
734   // Step 3.
735   var n = ToInteger(count);
737   // Step 4.
738   if (n < 0) {
739     ThrowRangeError(JSMSG_NEGATIVE_REPETITION_COUNT);
740   }
742   // Step 5.
743   // Inverted condition to handle |Infinity * 0 = NaN| correctly.
744   if (!(n * S.length <= MAX_STRING_LENGTH)) {
745     ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE);
746   }
748   // Communicate |n|'s possible range to the compiler. We actually use
749   // MAX_STRING_LENGTH + 1 as range because that's a valid bit mask. That's
750   // fine because it's only used as optimization hint.
751   assert(
752     TO_INT32(MAX_STRING_LENGTH + 1) === MAX_STRING_LENGTH + 1,
753     "MAX_STRING_LENGTH + 1 must fit in int32"
754   );
755   assert(
756     ((MAX_STRING_LENGTH + 1) & (MAX_STRING_LENGTH + 2)) === 0,
757     "MAX_STRING_LENGTH + 1 can be used as a bitmask"
758   );
759   n = n & (MAX_STRING_LENGTH + 1);
761   // Steps 6-7.
762   var T = "";
763   for (;;) {
764     if (n & 1) {
765       T += S;
766     }
767     n >>= 1;
768     if (n) {
769       S += S;
770     } else {
771       break;
772     }
773   }
774   return T;
777 // ES6 draft specification, section 21.1.3.27, version 2013-09-27.
778 function String_iterator() {
779   // Step 1.
780   if (IsNullOrUndefined(this)) {
781     ThrowTypeError(
782       JSMSG_INCOMPATIBLE_PROTO2,
783       "String",
784       "Symbol.iterator",
785       ToString(this)
786     );
787   }
789   // Step 2.
790   var S = ToString(this);
792   // Step 3.
793   var iterator = NewStringIterator();
794   UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, S);
795   UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0);
796   return iterator;
799 function StringIteratorNext() {
800   var obj = this;
801   if (!IsObject(obj) || (obj = GuardToStringIterator(obj)) === null) {
802     return callFunction(
803       CallStringIteratorMethodIfWrapped,
804       this,
805       "StringIteratorNext"
806     );
807   }
809   var S = UnsafeGetStringFromReservedSlot(obj, ITERATOR_SLOT_TARGET);
810   // We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in
811   // SelfHostring.cpp) so our current index can never be anything other than
812   // an Int32Value.
813   var index = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX);
814   var size = S.length;
815   var result = { value: undefined, done: false };
817   if (index >= size) {
818     result.done = true;
819     return result;
820   }
822   var codePoint = callFunction(std_String_codePointAt, S, index);
823   var charCount = 1 + (codePoint > 0xffff);
825   UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + charCount);
827   result.value = callFunction(std_String_fromCodePoint, null, codePoint);
829   return result;
831 SetIsInlinableLargeFunction(StringIteratorNext);
833 #if JS_HAS_INTL_API
834 var collatorCache = new_Record();
837  * Compare this String against that String, using the locale and collation
838  * options provided.
840  * Spec: ECMAScript Internationalization API Specification, 13.1.1.
841  */
842 function String_localeCompare(that) {
843   // Step 1.
844   if (IsNullOrUndefined(this)) {
845     ThrowIncompatibleMethod("localeCompare", this);
846   }
848   // Steps 2-3.
849   var S = ToString(this);
850   var That = ToString(that);
852   // Steps 4-5.
853   var locales = ArgumentsLength() > 1 ? GetArgument(1) : undefined;
854   var options = ArgumentsLength() > 2 ? GetArgument(2) : undefined;
856   // Step 6.
857   var collator;
858   if (locales === undefined && options === undefined) {
859     // This cache only optimizes for the old ES5 localeCompare without
860     // locales and options.
861     if (!intl_IsRuntimeDefaultLocale(collatorCache.runtimeDefaultLocale)) {
862       collatorCache.collator = intl_Collator(locales, options);
863       collatorCache.runtimeDefaultLocale = intl_RuntimeDefaultLocale();
864     }
865     collator = collatorCache.collator;
866   } else {
867     collator = intl_Collator(locales, options);
868   }
870   // Step 7.
871   return intl_CompareStrings(collator, S, That);
875  * 13.1.2 String.prototype.toLocaleLowerCase ( [ locales ] )
877  * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
878  */
879 function String_toLocaleLowerCase() {
880   // Step 1.
881   if (IsNullOrUndefined(this)) {
882     ThrowIncompatibleMethod("toLocaleLowerCase", this);
883   }
885   // Step 2.
886   var string = ToString(this);
888   // Handle the common cases (no locales argument or a single string
889   // argument) first.
890   var locales = ArgumentsLength() ? GetArgument(0) : undefined;
891   var requestedLocale;
892   if (locales === undefined) {
893     // Steps 3, 6.
894     requestedLocale = undefined;
895   } else if (typeof locales === "string") {
896     // Steps 3, 5.
897     requestedLocale = intl_ValidateAndCanonicalizeLanguageTag(locales, false);
898   } else {
899     // Step 3.
900     var requestedLocales = CanonicalizeLocaleList(locales);
902     // Steps 4-6.
903     requestedLocale = requestedLocales.length ? requestedLocales[0] : undefined;
904   }
906   // Trivial case: When the input is empty, directly return the empty string.
907   if (string.length === 0) {
908     return "";
909   }
911   if (requestedLocale === undefined) {
912     requestedLocale = DefaultLocale();
913   }
915   // Steps 7-16.
916   return intl_toLocaleLowerCase(string, requestedLocale);
920  * 13.1.3 String.prototype.toLocaleUpperCase ( [ locales ] )
922  * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
923  */
924 function String_toLocaleUpperCase() {
925   // Step 1.
926   if (IsNullOrUndefined(this)) {
927     ThrowIncompatibleMethod("toLocaleUpperCase", this);
928   }
930   // Step 2.
931   var string = ToString(this);
933   // Handle the common cases (no locales argument or a single string
934   // argument) first.
935   var locales = ArgumentsLength() ? GetArgument(0) : undefined;
936   var requestedLocale;
937   if (locales === undefined) {
938     // Steps 3, 6.
939     requestedLocale = undefined;
940   } else if (typeof locales === "string") {
941     // Steps 3, 5.
942     requestedLocale = intl_ValidateAndCanonicalizeLanguageTag(locales, false);
943   } else {
944     // Step 3.
945     var requestedLocales = CanonicalizeLocaleList(locales);
947     // Steps 4-6.
948     requestedLocale = requestedLocales.length ? requestedLocales[0] : undefined;
949   }
951   // Trivial case: When the input is empty, directly return the empty string.
952   if (string.length === 0) {
953     return "";
954   }
956   if (requestedLocale === undefined) {
957     requestedLocale = DefaultLocale();
958   }
960   // Steps 7-16.
961   return intl_toLocaleUpperCase(string, requestedLocale);
963 #endif  // JS_HAS_INTL_API
965 // ES2018 draft rev 8fadde42cf6a9879b4ab0cb6142b31c4ee501667
966 // 21.1.2.4 String.raw ( template, ...substitutions )
967 function String_static_raw(callSite /*, ...substitutions*/) {
968   // Steps 1-2 (not applicable).
970   // Step 3.
971   var cooked = ToObject(callSite);
973   // Step 4.
974   var raw = ToObject(cooked.raw);
976   // Step 5.
977   var literalSegments = ToLength(raw.length);
979   // Step 6.
980   if (literalSegments === 0) {
981     return "";
982   }
984   // Special case for |String.raw `<literal>`| callers to avoid falling into
985   // the loop code below.
986   if (literalSegments === 1) {
987     return ToString(raw[0]);
988   }
990   // Steps 7-9 were reordered to use ArgumentsLength/GetArgument instead of a
991   // rest parameter, because the former is currently more optimized.
992   //
993   // String.raw intersperses the substitution elements between the literal
994   // segments, i.e. a substitution is added iff there are still pending
995   // literal segments. Furthermore by moving the access to |raw[0]| outside
996   // of the loop, we can use |nextIndex| to index into both, the |raw| array
997   // and the arguments.
999   // Steps 7 (implicit) and 9.a-c.
1000   var resultString = ToString(raw[0]);
1002   // Steps 8-9, 9.d, and 9.i.
1003   for (var nextIndex = 1; nextIndex < literalSegments; nextIndex++) {
1004     // Steps 9.e-h.
1005     if (nextIndex < ArgumentsLength()) {
1006       resultString += ToString(GetArgument(nextIndex));
1007     }
1009     // Steps 9.a-c.
1010     resultString += ToString(raw[nextIndex]);
1011   }
1013   // Step 9.d.i.
1014   return resultString;
1017 // ES6 draft 2014-04-27 B.2.3.3
1018 function String_big() {
1019   if (IsNullOrUndefined(this)) {
1020     ThrowIncompatibleMethod("big", this);
1021   }
1022   return "<big>" + ToString(this) + "</big>";
1025 // ES6 draft 2014-04-27 B.2.3.4
1026 function String_blink() {
1027   if (IsNullOrUndefined(this)) {
1028     ThrowIncompatibleMethod("blink", this);
1029   }
1030   return "<blink>" + ToString(this) + "</blink>";
1033 // ES6 draft 2014-04-27 B.2.3.5
1034 function String_bold() {
1035   if (IsNullOrUndefined(this)) {
1036     ThrowIncompatibleMethod("bold", this);
1037   }
1038   return "<b>" + ToString(this) + "</b>";
1041 // ES6 draft 2014-04-27 B.2.3.6
1042 function String_fixed() {
1043   if (IsNullOrUndefined(this)) {
1044     ThrowIncompatibleMethod("fixed", this);
1045   }
1046   return "<tt>" + ToString(this) + "</tt>";
1049 // ES6 draft 2014-04-27 B.2.3.9
1050 function String_italics() {
1051   if (IsNullOrUndefined(this)) {
1052     ThrowIncompatibleMethod("italics", this);
1053   }
1054   return "<i>" + ToString(this) + "</i>";
1057 // ES6 draft 2014-04-27 B.2.3.11
1058 function String_small() {
1059   if (IsNullOrUndefined(this)) {
1060     ThrowIncompatibleMethod("small", this);
1061   }
1062   return "<small>" + ToString(this) + "</small>";
1065 // ES6 draft 2014-04-27 B.2.3.12
1066 function String_strike() {
1067   if (IsNullOrUndefined(this)) {
1068     ThrowIncompatibleMethod("strike", this);
1069   }
1070   return "<strike>" + ToString(this) + "</strike>";
1073 // ES6 draft 2014-04-27 B.2.3.13
1074 function String_sub() {
1075   if (IsNullOrUndefined(this)) {
1076     ThrowIncompatibleMethod("sub", this);
1077   }
1078   return "<sub>" + ToString(this) + "</sub>";
1081 // ES6 draft 2014-04-27 B.2.3.14
1082 function String_sup() {
1083   if (IsNullOrUndefined(this)) {
1084     ThrowIncompatibleMethod("sup", this);
1085   }
1086   return "<sup>" + ToString(this) + "</sup>";
1089 function EscapeAttributeValue(v) {
1090   var inputStr = ToString(v);
1091   return StringReplaceAllString(inputStr, '"', "&quot;");
1094 // ES6 draft 2014-04-27 B.2.3.2
1095 function String_anchor(name) {
1096   if (IsNullOrUndefined(this)) {
1097     ThrowIncompatibleMethod("anchor", this);
1098   }
1099   var S = ToString(this);
1100   return '<a name="' + EscapeAttributeValue(name) + '">' + S + "</a>";
1103 // ES6 draft 2014-04-27 B.2.3.7
1104 function String_fontcolor(color) {
1105   if (IsNullOrUndefined(this)) {
1106     ThrowIncompatibleMethod("fontcolor", this);
1107   }
1108   var S = ToString(this);
1109   return '<font color="' + EscapeAttributeValue(color) + '">' + S + "</font>";
1112 // ES6 draft 2014-04-27 B.2.3.8
1113 function String_fontsize(size) {
1114   if (IsNullOrUndefined(this)) {
1115     ThrowIncompatibleMethod("fontsize", this);
1116   }
1117   var S = ToString(this);
1118   return '<font size="' + EscapeAttributeValue(size) + '">' + S + "</font>";
1121 // ES6 draft 2014-04-27 B.2.3.10
1122 function String_link(url) {
1123   if (IsNullOrUndefined(this)) {
1124     ThrowIncompatibleMethod("link", this);
1125   }
1126   var S = ToString(this);
1127   return '<a href="' + EscapeAttributeValue(url) + '">' + S + "</a>";