blob: a4019784e8ec1239ded42118be62df9eb6bd204c [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
12var ArrayIndexOf;
13var ArrayJoin;
14var GlobalRegExp = global.RegExp;
15var GlobalString = global.String;
16var InternalArray = utils.InternalArray;
17var InternalPackedArray = utils.InternalPackedArray;
18var MakeRangeError;
19var MakeTypeError;
Ben Murdoch097c5b22016-05-18 11:27:45 +010020var MaxSimple;
21var MinSimple;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000022var matchSymbol = utils.ImportNow("match_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000023var RegExpExecNoTests;
Ben Murdoch097c5b22016-05-18 11:27:45 +010024var replaceSymbol = utils.ImportNow("replace_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000025var searchSymbol = utils.ImportNow("search_symbol");
26var splitSymbol = utils.ImportNow("split_symbol");
27
28utils.Import(function(from) {
29 ArrayIndexOf = from.ArrayIndexOf;
30 ArrayJoin = from.ArrayJoin;
31 MakeRangeError = from.MakeRangeError;
32 MakeTypeError = from.MakeTypeError;
Ben Murdoch097c5b22016-05-18 11:27:45 +010033 MaxSimple = from.MaxSimple;
34 MinSimple = from.MinSimple;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000035 RegExpExecNoTests = from.RegExpExecNoTests;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000036});
37
38//-------------------------------------------------------------------
39
40// ECMA-262 section 15.5.4.2
41function 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
50function 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.4
59function StringCharAtJS(pos) {
60 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
61
62 var result = %_StringCharAt(this, pos);
63 if (%_IsSmi(result)) {
64 result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos));
65 }
66 return result;
67}
68
69
70// ECMA-262 section 15.5.4.5
71function StringCharCodeAtJS(pos) {
72 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
73
74 var result = %_StringCharCodeAt(this, pos);
75 if (!%_IsSmi(result)) {
76 result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos));
77 }
78 return result;
79}
80
81
82// ECMA-262, section 15.5.4.6
83function StringConcat(other /* and more */) { // length == 1
Ben Murdoch097c5b22016-05-18 11:27:45 +010084 "use strict";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000085 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
Ben Murdoch097c5b22016-05-18 11:27:45 +010086 var s = TO_STRING(this);
87 var len = arguments.length;
88 for (var i = 0; i < len; ++i) {
89 s = s + TO_STRING(arguments[i]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000090 }
Ben Murdoch097c5b22016-05-18 11:27:45 +010091 return s;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000092}
93
94
95// ECMA-262 section 15.5.4.7
Ben Murdoch097c5b22016-05-18 11:27:45 +010096function StringIndexOf(pattern, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000097 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
98
99 var subject = TO_STRING(this);
100 pattern = TO_STRING(pattern);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100101 var index = TO_INTEGER(position);
102 if (index < 0) index = 0;
103 if (index > subject.length) index = subject.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000104 return %StringIndexOf(subject, pattern, index);
105}
106
Ben Murdoch097c5b22016-05-18 11:27:45 +0100107%FunctionSetLength(StringIndexOf, 1);
108
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109
110// ECMA-262 section 15.5.4.8
Ben Murdoch097c5b22016-05-18 11:27:45 +0100111function StringLastIndexOf(pat, pos) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000112 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
113
114 var sub = TO_STRING(this);
115 var subLength = sub.length;
116 var pat = TO_STRING(pat);
117 var patLength = pat.length;
118 var index = subLength - patLength;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100119 var position = TO_NUMBER(pos);
120 if (!NUMBER_IS_NAN(position)) {
121 position = TO_INTEGER(position);
122 if (position < 0) {
123 position = 0;
124 }
125 if (position + patLength < subLength) {
126 index = position;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000127 }
128 }
129 if (index < 0) {
130 return -1;
131 }
132 return %StringLastIndexOf(sub, pat, index);
133}
134
Ben Murdoch097c5b22016-05-18 11:27:45 +0100135%FunctionSetLength(StringLastIndexOf, 1);
136
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000137
138// ECMA-262 section 15.5.4.9
139//
140// This function is implementation specific. For now, we do not
141// do anything locale specific.
142function StringLocaleCompareJS(other) {
143 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
144
145 return %StringLocaleCompare(TO_STRING(this), TO_STRING(other));
146}
147
148
149// ES6 21.1.3.11.
150function StringMatchJS(pattern) {
151 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
152
153 if (!IS_NULL_OR_UNDEFINED(pattern)) {
154 var matcher = pattern[matchSymbol];
155 if (!IS_UNDEFINED(matcher)) {
156 return %_Call(matcher, pattern, this);
157 }
158 }
159
160 var subject = TO_STRING(this);
161
162 // Non-regexp argument.
163 var regexp = new GlobalRegExp(pattern);
164 return RegExpExecNoTests(regexp, subject, 0);
165}
166
167
168// ECMA-262 v6, section 21.1.3.12
169//
170// For now we do nothing, as proper normalization requires big tables.
171// If Intl is enabled, then i18n.js will override it and provide the the
172// proper functionality.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100173function StringNormalize(formArg) { // length == 0
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000174 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
175 var s = TO_STRING(this);
176
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000177 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
178
179 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
180 var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
181 if (normalizationForm === -1) {
182 throw MakeRangeError(kNormalizationForm,
183 %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
184 }
185
186 return s;
187}
188
Ben Murdoch097c5b22016-05-18 11:27:45 +0100189%FunctionSetLength(StringNormalize, 0);
190
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000191
192// This has the same size as the RegExpLastMatchInfo array, and can be used
193// for functions that expect that structure to be returned. It is used when
194// the needle is a string rather than a regexp. In this case we can't update
195// lastMatchArray without erroneously affecting the properties on the global
196// RegExp object.
197var reusableMatchInfo = [2, "", "", -1, -1];
198
199
Ben Murdoch097c5b22016-05-18 11:27:45 +0100200// ES6, section 21.1.3.14
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000201function StringReplace(search, replace) {
202 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
203
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000204 // Decision tree for dispatch
Ben Murdoch097c5b22016-05-18 11:27:45 +0100205 // .. regexp search (in src/js/regexp.js, RegExpReplace)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000206 // .... string replace
207 // ...... non-global search
208 // ........ empty string replace
209 // ........ non-empty string replace (with $-expansion)
210 // ...... global search
211 // ........ no need to circumvent last match info override
212 // ........ need to circument last match info override
213 // .... function replace
214 // ...... global search
215 // ...... non-global search
216 // .. string search
217 // .... special case that replaces with one single character
218 // ...... function replace
219 // ...... string replace (with $-expansion)
220
Ben Murdoch097c5b22016-05-18 11:27:45 +0100221 if (!IS_NULL_OR_UNDEFINED(search)) {
222 var replacer = search[replaceSymbol];
223 if (!IS_UNDEFINED(replacer)) {
224 return %_Call(replacer, search, this, replace);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000225 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000226 }
227
Ben Murdoch097c5b22016-05-18 11:27:45 +0100228 var subject = TO_STRING(this);
229
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000230 search = TO_STRING(search);
231
232 if (search.length == 1 &&
233 subject.length > 0xFF &&
234 IS_STRING(replace) &&
235 %StringIndexOf(replace, '$', 0) < 0) {
236 // Searching by traversing a cons string tree and replace with cons of
237 // slices works only when the replaced string is a single character, being
238 // replaced by a simple string and only pays off for long strings.
239 return %StringReplaceOneCharWithString(subject, search, replace);
240 }
241 var start = %StringIndexOf(subject, search, 0);
242 if (start < 0) return subject;
243 var end = start + search.length;
244
245 var result = %_SubString(subject, 0, start);
246
247 // Compute the string to replace with.
248 if (IS_CALLABLE(replace)) {
249 result += replace(search, start, subject);
250 } else {
251 reusableMatchInfo[CAPTURE0] = start;
252 reusableMatchInfo[CAPTURE1] = end;
253 result = ExpandReplacement(TO_STRING(replace),
254 subject,
255 reusableMatchInfo,
256 result);
257 }
258
259 return result + %_SubString(subject, end, subject.length);
260}
261
262
263// Expand the $-expressions in the string and return a new string with
264// the result.
265function ExpandReplacement(string, subject, matchInfo, result) {
266 var length = string.length;
267 var next = %StringIndexOf(string, '$', 0);
268 if (next < 0) {
269 if (length > 0) result += string;
270 return result;
271 }
272
273 if (next > 0) result += %_SubString(string, 0, next);
274
275 while (true) {
276 var expansion = '$';
277 var position = next + 1;
278 if (position < length) {
279 var peek = %_StringCharCodeAt(string, position);
280 if (peek == 36) { // $$
281 ++position;
282 result += '$';
283 } else if (peek == 38) { // $& - match
284 ++position;
285 result +=
286 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
287 } else if (peek == 96) { // $` - prefix
288 ++position;
289 result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
290 } else if (peek == 39) { // $' - suffix
291 ++position;
292 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
293 } else if (peek >= 48 && peek <= 57) {
294 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
295 var scaled_index = (peek - 48) << 1;
296 var advance = 1;
297 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
298 if (position + 1 < string.length) {
299 var next = %_StringCharCodeAt(string, position + 1);
300 if (next >= 48 && next <= 57) {
301 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
302 if (new_scaled_index < number_of_captures) {
303 scaled_index = new_scaled_index;
304 advance = 2;
305 }
306 }
307 }
308 if (scaled_index != 0 && scaled_index < number_of_captures) {
309 var start = matchInfo[CAPTURE(scaled_index)];
310 if (start >= 0) {
311 result +=
312 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
313 }
314 position += advance;
315 } else {
316 result += '$';
317 }
318 } else {
319 result += '$';
320 }
321 } else {
322 result += '$';
323 }
324
325 // Go the the next $ in the string.
326 next = %StringIndexOf(string, '$', position);
327
328 // Return if there are no more $ characters in the string. If we
329 // haven't reached the end, we need to append the suffix.
330 if (next < 0) {
331 if (position < length) {
332 result += %_SubString(string, position, length);
333 }
334 return result;
335 }
336
337 // Append substring between the previous and the next $ character.
338 if (next > position) {
339 result += %_SubString(string, position, next);
340 }
341 }
342 return result;
343}
344
345
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000346// ES6 21.1.3.15.
347function StringSearch(pattern) {
348 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
349
350 if (!IS_NULL_OR_UNDEFINED(pattern)) {
351 var searcher = pattern[searchSymbol];
352 if (!IS_UNDEFINED(searcher)) {
353 return %_Call(searcher, pattern, this);
354 }
355 }
356
357 var subject = TO_STRING(this);
358 var regexp = new GlobalRegExp(pattern);
359 return %_Call(regexp[searchSymbol], regexp, subject);
360}
361
362
363// ECMA-262 section 15.5.4.13
364function StringSlice(start, end) {
365 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
366
367 var s = TO_STRING(this);
368 var s_len = s.length;
369 var start_i = TO_INTEGER(start);
370 var end_i = s_len;
371 if (!IS_UNDEFINED(end)) {
372 end_i = TO_INTEGER(end);
373 }
374
375 if (start_i < 0) {
376 start_i += s_len;
377 if (start_i < 0) {
378 start_i = 0;
379 }
380 } else {
381 if (start_i > s_len) {
382 return '';
383 }
384 }
385
386 if (end_i < 0) {
387 end_i += s_len;
388 if (end_i < 0) {
389 return '';
390 }
391 } else {
392 if (end_i > s_len) {
393 end_i = s_len;
394 }
395 }
396
397 if (end_i <= start_i) {
398 return '';
399 }
400
401 return %_SubString(s, start_i, end_i);
402}
403
404
405// ES6 21.1.3.17.
406function StringSplitJS(separator, limit) {
407 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
408
409 if (!IS_NULL_OR_UNDEFINED(separator)) {
410 var splitter = separator[splitSymbol];
411 if (!IS_UNDEFINED(splitter)) {
412 return %_Call(splitter, separator, this, limit);
413 }
414 }
415
416 var subject = TO_STRING(this);
417 limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
418
419 var length = subject.length;
420 var separator_string = TO_STRING(separator);
421
422 if (limit === 0) return [];
423
424 // ECMA-262 says that if separator is undefined, the result should
425 // be an array of size 1 containing the entire string.
426 if (IS_UNDEFINED(separator)) return [subject];
427
428 var separator_length = separator_string.length;
429
430 // If the separator string is empty then return the elements in the subject.
431 if (separator_length === 0) return %StringToArray(subject, limit);
432
433 return %StringSplit(subject, separator_string, limit);
434}
435
436
437// ECMA-262 section 15.5.4.15
438function StringSubstring(start, end) {
439 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
440
441 var s = TO_STRING(this);
442 var s_len = s.length;
443
444 var start_i = TO_INTEGER(start);
445 if (start_i < 0) {
446 start_i = 0;
447 } else if (start_i > s_len) {
448 start_i = s_len;
449 }
450
451 var end_i = s_len;
452 if (!IS_UNDEFINED(end)) {
453 end_i = TO_INTEGER(end);
454 if (end_i > s_len) {
455 end_i = s_len;
456 } else {
457 if (end_i < 0) end_i = 0;
458 if (start_i > end_i) {
459 var tmp = end_i;
460 end_i = start_i;
461 start_i = tmp;
462 }
463 }
464 }
465
466 return %_SubString(s, start_i, end_i);
467}
468
469
470// ES6 draft, revision 26 (2014-07-18), section B.2.3.1
471function StringSubstr(start, n) {
472 CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
473
474 var s = TO_STRING(this);
475 var len;
476
477 // Correct n: If not given, set to string length; if explicitly
478 // set to undefined, zero, or negative, returns empty string.
479 if (IS_UNDEFINED(n)) {
480 len = s.length;
481 } else {
482 len = TO_INTEGER(n);
483 if (len <= 0) return '';
484 }
485
486 // Correct start: If not given (or undefined), set to zero; otherwise
487 // convert to integer and handle negative case.
488 if (IS_UNDEFINED(start)) {
489 start = 0;
490 } else {
491 start = TO_INTEGER(start);
492 // If positive, and greater than or equal to the string length,
493 // return empty string.
494 if (start >= s.length) return '';
495 // If negative and absolute value is larger than the string length,
496 // use zero.
497 if (start < 0) {
498 start += s.length;
499 if (start < 0) start = 0;
500 }
501 }
502
503 var end = start + len;
504 if (end > s.length) end = s.length;
505
506 return %_SubString(s, start, end);
507}
508
509
510// ECMA-262, 15.5.4.16
511function StringToLowerCaseJS() {
512 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
513
514 return %StringToLowerCase(TO_STRING(this));
515}
516
517
518// ECMA-262, 15.5.4.17
519function StringToLocaleLowerCase() {
520 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
521
522 return %StringToLowerCase(TO_STRING(this));
523}
524
525
526// ECMA-262, 15.5.4.18
527function StringToUpperCaseJS() {
528 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
529
530 return %StringToUpperCase(TO_STRING(this));
531}
532
533
534// ECMA-262, 15.5.4.19
535function StringToLocaleUpperCase() {
536 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
537
538 return %StringToUpperCase(TO_STRING(this));
539}
540
541// ES5, 15.5.4.20
542function StringTrimJS() {
543 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
544
545 return %StringTrim(TO_STRING(this), true, true);
546}
547
548function StringTrimLeft() {
549 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
550
551 return %StringTrim(TO_STRING(this), true, false);
552}
553
554function StringTrimRight() {
555 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
556
557 return %StringTrim(TO_STRING(this), false, true);
558}
559
560
561// ECMA-262, section 15.5.3.2
Ben Murdoch097c5b22016-05-18 11:27:45 +0100562function StringFromCharCode(_) { // length == 1
563 "use strict";
564 var s = "";
565 var n = arguments.length;
566 for (var i = 0; i < n; ++i) {
567 s += %_StringCharFromCode(arguments[i] & 0xffff);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000568 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100569 return s;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000570}
571
572
573// ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
574function HtmlEscape(str) {
575 return %_Call(StringReplace, TO_STRING(str), /"/g, "&quot;");
576}
577
578
579// ES6 draft, revision 26 (2014-07-18), section B.2.3.2
580function StringAnchor(name) {
581 CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor");
582 return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) +
583 "</a>";
584}
585
586
587// ES6 draft, revision 26 (2014-07-18), section B.2.3.3
588function StringBig() {
589 CHECK_OBJECT_COERCIBLE(this, "String.prototype.big");
590 return "<big>" + TO_STRING(this) + "</big>";
591}
592
593
594// ES6 draft, revision 26 (2014-07-18), section B.2.3.4
595function StringBlink() {
596 CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink");
597 return "<blink>" + TO_STRING(this) + "</blink>";
598}
599
600
601// ES6 draft, revision 26 (2014-07-18), section B.2.3.5
602function StringBold() {
603 CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold");
604 return "<b>" + TO_STRING(this) + "</b>";
605}
606
607
608// ES6 draft, revision 26 (2014-07-18), section B.2.3.6
609function StringFixed() {
610 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed");
611 return "<tt>" + TO_STRING(this) + "</tt>";
612}
613
614
615// ES6 draft, revision 26 (2014-07-18), section B.2.3.7
616function StringFontcolor(color) {
617 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor");
618 return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) +
619 "</font>";
620}
621
622
623// ES6 draft, revision 26 (2014-07-18), section B.2.3.8
624function StringFontsize(size) {
625 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize");
626 return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) +
627 "</font>";
628}
629
630
631// ES6 draft, revision 26 (2014-07-18), section B.2.3.9
632function StringItalics() {
633 CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics");
634 return "<i>" + TO_STRING(this) + "</i>";
635}
636
637
638// ES6 draft, revision 26 (2014-07-18), section B.2.3.10
639function StringLink(s) {
640 CHECK_OBJECT_COERCIBLE(this, "String.prototype.link");
641 return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>";
642}
643
644
645// ES6 draft, revision 26 (2014-07-18), section B.2.3.11
646function StringSmall() {
647 CHECK_OBJECT_COERCIBLE(this, "String.prototype.small");
648 return "<small>" + TO_STRING(this) + "</small>";
649}
650
651
652// ES6 draft, revision 26 (2014-07-18), section B.2.3.12
653function StringStrike() {
654 CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike");
655 return "<strike>" + TO_STRING(this) + "</strike>";
656}
657
658
659// ES6 draft, revision 26 (2014-07-18), section B.2.3.13
660function StringSub() {
661 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub");
662 return "<sub>" + TO_STRING(this) + "</sub>";
663}
664
665
666// ES6 draft, revision 26 (2014-07-18), section B.2.3.14
667function StringSup() {
668 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup");
669 return "<sup>" + TO_STRING(this) + "</sup>";
670}
671
672// ES6, section 21.1.3.13
673function StringRepeat(count) {
674 CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat");
675
676 var s = TO_STRING(this);
677 var n = TO_INTEGER(count);
678
679 if (n < 0 || n === INFINITY) throw MakeRangeError(kInvalidCountValue);
680
681 // Early return to allow an arbitrarily-large repeat of the empty string.
682 if (s.length === 0) return "";
683
684 // The maximum string length is stored in a smi, so a longer repeat
685 // must result in a range error.
686 if (n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue);
687
688 var r = "";
689 while (true) {
690 if (n & 1) r += s;
691 n >>= 1;
692 if (n === 0) return r;
693 s += s;
694 }
695}
696
697
698// ES6 draft 04-05-14, section 21.1.3.18
Ben Murdoch097c5b22016-05-18 11:27:45 +0100699function StringStartsWith(searchString, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000700 CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith");
701
702 var s = TO_STRING(this);
703
704 if (IS_REGEXP(searchString)) {
705 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith");
706 }
707
708 var ss = TO_STRING(searchString);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100709 var pos = TO_INTEGER(position);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000710
711 var s_len = s.length;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100712 var start = MinSimple(MaxSimple(pos, 0), s_len);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000713 var ss_len = ss.length;
714 if (ss_len + start > s_len) {
715 return false;
716 }
717
718 return %_SubString(s, start, start + ss_len) === ss;
719}
720
Ben Murdoch097c5b22016-05-18 11:27:45 +0100721%FunctionSetLength(StringStartsWith, 1);
722
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000723
724// ES6 draft 04-05-14, section 21.1.3.7
Ben Murdoch097c5b22016-05-18 11:27:45 +0100725function StringEndsWith(searchString, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000726 CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith");
727
728 var s = TO_STRING(this);
729
730 if (IS_REGEXP(searchString)) {
731 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith");
732 }
733
734 var ss = TO_STRING(searchString);
735 var s_len = s.length;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100736 var pos = !IS_UNDEFINED(position) ? TO_INTEGER(position) : s_len
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000737
Ben Murdoch097c5b22016-05-18 11:27:45 +0100738 var end = MinSimple(MaxSimple(pos, 0), s_len);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000739 var ss_len = ss.length;
740 var start = end - ss_len;
741 if (start < 0) {
742 return false;
743 }
744
745 return %_SubString(s, start, start + ss_len) === ss;
746}
747
Ben Murdoch097c5b22016-05-18 11:27:45 +0100748%FunctionSetLength(StringEndsWith, 1);
749
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000750
751// ES6 draft 04-05-14, section 21.1.3.6
Ben Murdoch097c5b22016-05-18 11:27:45 +0100752function StringIncludes(searchString, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000753 CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes");
754
755 var string = TO_STRING(this);
756
757 if (IS_REGEXP(searchString)) {
758 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes");
759 }
760
761 searchString = TO_STRING(searchString);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100762 var pos = TO_INTEGER(position);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000763
764 var stringLength = string.length;
765 if (pos < 0) pos = 0;
766 if (pos > stringLength) pos = stringLength;
767 var searchStringLength = searchString.length;
768
769 if (searchStringLength + pos > stringLength) {
770 return false;
771 }
772
773 return %StringIndexOf(string, searchString, pos) !== -1;
774}
775
Ben Murdoch097c5b22016-05-18 11:27:45 +0100776%FunctionSetLength(StringIncludes, 1);
777
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000778
779// ES6 Draft 05-22-2014, section 21.1.3.3
780function StringCodePointAt(pos) {
781 CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
782
783 var string = TO_STRING(this);
784 var size = string.length;
785 pos = TO_INTEGER(pos);
786 if (pos < 0 || pos >= size) {
787 return UNDEFINED;
788 }
789 var first = %_StringCharCodeAt(string, pos);
790 if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
791 return first;
792 }
793 var second = %_StringCharCodeAt(string, pos + 1);
794 if (second < 0xDC00 || second > 0xDFFF) {
795 return first;
796 }
797 return (first - 0xD800) * 0x400 + second + 0x2400;
798}
799
800
801// ES6 Draft 05-22-2014, section 21.1.2.2
802function StringFromCodePoint(_) { // length = 1
Ben Murdoch097c5b22016-05-18 11:27:45 +0100803 "use strict";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000804 var code;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100805 var length = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000806 var index;
807 var result = "";
808 for (index = 0; index < length; index++) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100809 code = arguments[index];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000810 if (!%_IsSmi(code)) {
811 code = TO_NUMBER(code);
812 }
813 if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
814 throw MakeRangeError(kInvalidCodePoint, code);
815 }
816 if (code <= 0xFFFF) {
817 result += %_StringCharFromCode(code);
818 } else {
819 code -= 0x10000;
820 result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800);
821 result += %_StringCharFromCode(code & 0x3FF | 0xDC00);
822 }
823 }
824 return result;
825}
826
827
828// -------------------------------------------------------------------
829// String methods related to templates
830
831// ES6 Draft 03-17-2015, section 21.1.2.4
832function StringRaw(callSite) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100833 "use strict";
834 var numberOfSubstitutions = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000835 var cooked = TO_OBJECT(callSite);
836 var raw = TO_OBJECT(cooked.raw);
837 var literalSegments = TO_LENGTH(raw.length);
838 if (literalSegments <= 0) return "";
839
840 var result = TO_STRING(raw[0]);
841
842 for (var i = 1; i < literalSegments; ++i) {
843 if (i < numberOfSubstitutions) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100844 result += TO_STRING(arguments[i]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000845 }
846 result += TO_STRING(raw[i]);
847 }
848
849 return result;
850}
851
852// -------------------------------------------------------------------
853
854// Set the String function and constructor.
855%FunctionSetPrototype(GlobalString, new GlobalString());
856
857// Set up the constructor property on the String prototype object.
858%AddNamedProperty(
859 GlobalString.prototype, "constructor", GlobalString, DONT_ENUM);
860
861// Set up the non-enumerable functions on the String object.
862utils.InstallFunctions(GlobalString, DONT_ENUM, [
863 "fromCharCode", StringFromCharCode,
864 "fromCodePoint", StringFromCodePoint,
865 "raw", StringRaw
866]);
867
868// Set up the non-enumerable functions on the String prototype object.
869utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
870 "valueOf", StringValueOf,
871 "toString", StringToString,
872 "charAt", StringCharAtJS,
873 "charCodeAt", StringCharCodeAtJS,
874 "codePointAt", StringCodePointAt,
875 "concat", StringConcat,
876 "endsWith", StringEndsWith,
877 "includes", StringIncludes,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100878 "indexOf", StringIndexOf,
879 "lastIndexOf", StringLastIndexOf,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000880 "localeCompare", StringLocaleCompareJS,
881 "match", StringMatchJS,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100882 "normalize", StringNormalize,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000883 "repeat", StringRepeat,
884 "replace", StringReplace,
885 "search", StringSearch,
886 "slice", StringSlice,
887 "split", StringSplitJS,
888 "substring", StringSubstring,
889 "substr", StringSubstr,
890 "startsWith", StringStartsWith,
891 "toLowerCase", StringToLowerCaseJS,
892 "toLocaleLowerCase", StringToLocaleLowerCase,
893 "toUpperCase", StringToUpperCaseJS,
894 "toLocaleUpperCase", StringToLocaleUpperCase,
895 "trim", StringTrimJS,
896 "trimLeft", StringTrimLeft,
897 "trimRight", StringTrimRight,
898
899 "link", StringLink,
900 "anchor", StringAnchor,
901 "fontcolor", StringFontcolor,
902 "fontsize", StringFontsize,
903 "big", StringBig,
904 "blink", StringBlink,
905 "bold", StringBold,
906 "fixed", StringFixed,
907 "italics", StringItalics,
908 "small", StringSmall,
909 "strike", StringStrike,
910 "sub", StringSub,
911 "sup", StringSup
912]);
913
914// -------------------------------------------------------------------
915// Exports
916
917utils.Export(function(to) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100918 to.ExpandReplacement = ExpandReplacement;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000919 to.StringCharAt = StringCharAtJS;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100920 to.StringIndexOf = StringIndexOf;
921 to.StringLastIndexOf = StringLastIndexOf;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000922 to.StringMatch = StringMatchJS;
923 to.StringReplace = StringReplace;
924 to.StringSlice = StringSlice;
925 to.StringSplit = StringSplitJS;
926 to.StringSubstr = StringSubstr;
927 to.StringSubstring = StringSubstring;
928});
929
930})