blob: 0eb394e173a62e4eacc9d48c233e27dcdfebe740 [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;
Ben Murdochda12d292016-06-02 14:46:10 +010018var IsRegExp;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000019var MakeRangeError;
20var MakeTypeError;
Ben Murdoch097c5b22016-05-18 11:27:45 +010021var MaxSimple;
22var MinSimple;
Ben Murdochda12d292016-06-02 14:46:10 +010023var RegExpInitialize;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000024var matchSymbol = utils.ImportNow("match_symbol");
Ben Murdoch097c5b22016-05-18 11:27:45 +010025var replaceSymbol = utils.ImportNow("replace_symbol");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000026var searchSymbol = utils.ImportNow("search_symbol");
27var splitSymbol = utils.ImportNow("split_symbol");
28
29utils.Import(function(from) {
30 ArrayIndexOf = from.ArrayIndexOf;
31 ArrayJoin = from.ArrayJoin;
Ben Murdochda12d292016-06-02 14:46:10 +010032 IsRegExp = from.IsRegExp;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000033 MakeRangeError = from.MakeRangeError;
34 MakeTypeError = from.MakeTypeError;
Ben Murdoch097c5b22016-05-18 11:27:45 +010035 MaxSimple = from.MaxSimple;
36 MinSimple = from.MinSimple;
Ben Murdochda12d292016-06-02 14:46:10 +010037 RegExpInitialize = from.RegExpInitialize;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000038});
39
40//-------------------------------------------------------------------
41
42// ECMA-262 section 15.5.4.2
43function StringToString() {
44 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
45 throw MakeTypeError(kNotGeneric, 'String.prototype.toString');
46 }
47 return %_ValueOf(this);
48}
49
50
51// ECMA-262 section 15.5.4.3
52function StringValueOf() {
53 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
54 throw MakeTypeError(kNotGeneric, 'String.prototype.valueOf');
55 }
56 return %_ValueOf(this);
57}
58
59
60// ECMA-262, section 15.5.4.4
61function StringCharAtJS(pos) {
62 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
63
64 var result = %_StringCharAt(this, pos);
65 if (%_IsSmi(result)) {
66 result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos));
67 }
68 return result;
69}
70
71
72// ECMA-262 section 15.5.4.5
73function StringCharCodeAtJS(pos) {
74 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
75
76 var result = %_StringCharCodeAt(this, pos);
77 if (!%_IsSmi(result)) {
78 result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos));
79 }
80 return result;
81}
82
83
84// ECMA-262, section 15.5.4.6
85function StringConcat(other /* and more */) { // length == 1
Ben Murdoch097c5b22016-05-18 11:27:45 +010086 "use strict";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000087 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
Ben Murdoch097c5b22016-05-18 11:27:45 +010088 var s = TO_STRING(this);
89 var len = arguments.length;
90 for (var i = 0; i < len; ++i) {
91 s = s + TO_STRING(arguments[i]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000092 }
Ben Murdoch097c5b22016-05-18 11:27:45 +010093 return s;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000094}
95
96
97// ECMA-262 section 15.5.4.7
Ben Murdoch097c5b22016-05-18 11:27:45 +010098function StringIndexOf(pattern, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000099 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
100
101 var subject = TO_STRING(this);
102 pattern = TO_STRING(pattern);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100103 var index = TO_INTEGER(position);
104 if (index < 0) index = 0;
105 if (index > subject.length) index = subject.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000106 return %StringIndexOf(subject, pattern, index);
107}
108
Ben Murdoch097c5b22016-05-18 11:27:45 +0100109%FunctionSetLength(StringIndexOf, 1);
110
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000111
112// ECMA-262 section 15.5.4.8
Ben Murdoch097c5b22016-05-18 11:27:45 +0100113function StringLastIndexOf(pat, pos) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000114 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
115
116 var sub = TO_STRING(this);
117 var subLength = sub.length;
118 var pat = TO_STRING(pat);
119 var patLength = pat.length;
120 var index = subLength - patLength;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100121 var position = TO_NUMBER(pos);
122 if (!NUMBER_IS_NAN(position)) {
123 position = TO_INTEGER(position);
124 if (position < 0) {
125 position = 0;
126 }
127 if (position + patLength < subLength) {
128 index = position;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000129 }
130 }
131 if (index < 0) {
132 return -1;
133 }
134 return %StringLastIndexOf(sub, pat, index);
135}
136
Ben Murdoch097c5b22016-05-18 11:27:45 +0100137%FunctionSetLength(StringLastIndexOf, 1);
138
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000139
140// ECMA-262 section 15.5.4.9
141//
142// This function is implementation specific. For now, we do not
143// do anything locale specific.
144function StringLocaleCompareJS(other) {
145 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
146
147 return %StringLocaleCompare(TO_STRING(this), TO_STRING(other));
148}
149
150
151// ES6 21.1.3.11.
152function StringMatchJS(pattern) {
153 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
154
155 if (!IS_NULL_OR_UNDEFINED(pattern)) {
156 var matcher = pattern[matchSymbol];
157 if (!IS_UNDEFINED(matcher)) {
158 return %_Call(matcher, pattern, this);
159 }
160 }
161
162 var subject = TO_STRING(this);
163
Ben Murdochda12d292016-06-02 14:46:10 +0100164 // Equivalent to RegExpCreate (ES#sec-regexpcreate)
165 var regexp = %_NewObject(GlobalRegExp, GlobalRegExp);
166 RegExpInitialize(regexp, pattern);
167 return regexp[matchSymbol](subject);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000168}
169
170
171// ECMA-262 v6, section 21.1.3.12
172//
173// For now we do nothing, as proper normalization requires big tables.
174// If Intl is enabled, then i18n.js will override it and provide the the
175// proper functionality.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100176function StringNormalize(formArg) { // length == 0
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000177 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
178 var s = TO_STRING(this);
179
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000180 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
181
182 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
183 var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
184 if (normalizationForm === -1) {
185 throw MakeRangeError(kNormalizationForm,
186 %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
187 }
188
189 return s;
190}
191
Ben Murdoch097c5b22016-05-18 11:27:45 +0100192%FunctionSetLength(StringNormalize, 0);
193
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000194
195// This has the same size as the RegExpLastMatchInfo array, and can be used
196// for functions that expect that structure to be returned. It is used when
197// the needle is a string rather than a regexp. In this case we can't update
198// lastMatchArray without erroneously affecting the properties on the global
199// RegExp object.
200var reusableMatchInfo = [2, "", "", -1, -1];
201
202
Ben Murdoch097c5b22016-05-18 11:27:45 +0100203// ES6, section 21.1.3.14
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000204function StringReplace(search, replace) {
205 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
206
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000207 // Decision tree for dispatch
Ben Murdoch097c5b22016-05-18 11:27:45 +0100208 // .. regexp search (in src/js/regexp.js, RegExpReplace)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000209 // .... string replace
210 // ...... non-global search
211 // ........ empty string replace
212 // ........ non-empty string replace (with $-expansion)
213 // ...... global search
214 // ........ no need to circumvent last match info override
215 // ........ need to circument last match info override
216 // .... function replace
217 // ...... global search
218 // ...... non-global search
219 // .. string search
220 // .... special case that replaces with one single character
221 // ...... function replace
222 // ...... string replace (with $-expansion)
223
Ben Murdoch097c5b22016-05-18 11:27:45 +0100224 if (!IS_NULL_OR_UNDEFINED(search)) {
225 var replacer = search[replaceSymbol];
226 if (!IS_UNDEFINED(replacer)) {
227 return %_Call(replacer, search, this, replace);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000228 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000229 }
230
Ben Murdoch097c5b22016-05-18 11:27:45 +0100231 var subject = TO_STRING(this);
232
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000233 search = TO_STRING(search);
234
235 if (search.length == 1 &&
236 subject.length > 0xFF &&
237 IS_STRING(replace) &&
238 %StringIndexOf(replace, '$', 0) < 0) {
239 // Searching by traversing a cons string tree and replace with cons of
240 // slices works only when the replaced string is a single character, being
241 // replaced by a simple string and only pays off for long strings.
242 return %StringReplaceOneCharWithString(subject, search, replace);
243 }
244 var start = %StringIndexOf(subject, search, 0);
245 if (start < 0) return subject;
246 var end = start + search.length;
247
248 var result = %_SubString(subject, 0, start);
249
250 // Compute the string to replace with.
251 if (IS_CALLABLE(replace)) {
252 result += replace(search, start, subject);
253 } else {
254 reusableMatchInfo[CAPTURE0] = start;
255 reusableMatchInfo[CAPTURE1] = end;
256 result = ExpandReplacement(TO_STRING(replace),
257 subject,
258 reusableMatchInfo,
259 result);
260 }
261
262 return result + %_SubString(subject, end, subject.length);
263}
264
265
266// Expand the $-expressions in the string and return a new string with
267// the result.
268function ExpandReplacement(string, subject, matchInfo, result) {
269 var length = string.length;
270 var next = %StringIndexOf(string, '$', 0);
271 if (next < 0) {
272 if (length > 0) result += string;
273 return result;
274 }
275
276 if (next > 0) result += %_SubString(string, 0, next);
277
278 while (true) {
279 var expansion = '$';
280 var position = next + 1;
281 if (position < length) {
282 var peek = %_StringCharCodeAt(string, position);
283 if (peek == 36) { // $$
284 ++position;
285 result += '$';
286 } else if (peek == 38) { // $& - match
287 ++position;
288 result +=
289 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
290 } else if (peek == 96) { // $` - prefix
291 ++position;
292 result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
293 } else if (peek == 39) { // $' - suffix
294 ++position;
295 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
296 } else if (peek >= 48 && peek <= 57) {
297 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
298 var scaled_index = (peek - 48) << 1;
299 var advance = 1;
300 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
301 if (position + 1 < string.length) {
302 var next = %_StringCharCodeAt(string, position + 1);
303 if (next >= 48 && next <= 57) {
304 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
305 if (new_scaled_index < number_of_captures) {
306 scaled_index = new_scaled_index;
307 advance = 2;
308 }
309 }
310 }
311 if (scaled_index != 0 && scaled_index < number_of_captures) {
312 var start = matchInfo[CAPTURE(scaled_index)];
313 if (start >= 0) {
314 result +=
315 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
316 }
317 position += advance;
318 } else {
319 result += '$';
320 }
321 } else {
322 result += '$';
323 }
324 } else {
325 result += '$';
326 }
327
328 // Go the the next $ in the string.
329 next = %StringIndexOf(string, '$', position);
330
331 // Return if there are no more $ characters in the string. If we
332 // haven't reached the end, we need to append the suffix.
333 if (next < 0) {
334 if (position < length) {
335 result += %_SubString(string, position, length);
336 }
337 return result;
338 }
339
340 // Append substring between the previous and the next $ character.
341 if (next > position) {
342 result += %_SubString(string, position, next);
343 }
344 }
345 return result;
346}
347
348
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000349// ES6 21.1.3.15.
350function StringSearch(pattern) {
351 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
352
353 if (!IS_NULL_OR_UNDEFINED(pattern)) {
354 var searcher = pattern[searchSymbol];
355 if (!IS_UNDEFINED(searcher)) {
356 return %_Call(searcher, pattern, this);
357 }
358 }
359
360 var subject = TO_STRING(this);
Ben Murdochda12d292016-06-02 14:46:10 +0100361
362 // Equivalent to RegExpCreate (ES#sec-regexpcreate)
363 var regexp = %_NewObject(GlobalRegExp, GlobalRegExp);
364 RegExpInitialize(regexp, pattern);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000365 return %_Call(regexp[searchSymbol], regexp, subject);
366}
367
368
369// ECMA-262 section 15.5.4.13
370function StringSlice(start, end) {
371 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
372
373 var s = TO_STRING(this);
374 var s_len = s.length;
375 var start_i = TO_INTEGER(start);
376 var end_i = s_len;
377 if (!IS_UNDEFINED(end)) {
378 end_i = TO_INTEGER(end);
379 }
380
381 if (start_i < 0) {
382 start_i += s_len;
383 if (start_i < 0) {
384 start_i = 0;
385 }
386 } else {
387 if (start_i > s_len) {
388 return '';
389 }
390 }
391
392 if (end_i < 0) {
393 end_i += s_len;
394 if (end_i < 0) {
395 return '';
396 }
397 } else {
398 if (end_i > s_len) {
399 end_i = s_len;
400 }
401 }
402
403 if (end_i <= start_i) {
404 return '';
405 }
406
407 return %_SubString(s, start_i, end_i);
408}
409
410
411// ES6 21.1.3.17.
412function StringSplitJS(separator, limit) {
413 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
414
415 if (!IS_NULL_OR_UNDEFINED(separator)) {
416 var splitter = separator[splitSymbol];
417 if (!IS_UNDEFINED(splitter)) {
418 return %_Call(splitter, separator, this, limit);
419 }
420 }
421
422 var subject = TO_STRING(this);
423 limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
424
425 var length = subject.length;
426 var separator_string = TO_STRING(separator);
427
428 if (limit === 0) return [];
429
430 // ECMA-262 says that if separator is undefined, the result should
431 // be an array of size 1 containing the entire string.
432 if (IS_UNDEFINED(separator)) return [subject];
433
434 var separator_length = separator_string.length;
435
436 // If the separator string is empty then return the elements in the subject.
437 if (separator_length === 0) return %StringToArray(subject, limit);
438
439 return %StringSplit(subject, separator_string, limit);
440}
441
442
443// ECMA-262 section 15.5.4.15
444function StringSubstring(start, end) {
445 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
446
447 var s = TO_STRING(this);
448 var s_len = s.length;
449
450 var start_i = TO_INTEGER(start);
451 if (start_i < 0) {
452 start_i = 0;
453 } else if (start_i > s_len) {
454 start_i = s_len;
455 }
456
457 var end_i = s_len;
458 if (!IS_UNDEFINED(end)) {
459 end_i = TO_INTEGER(end);
460 if (end_i > s_len) {
461 end_i = s_len;
462 } else {
463 if (end_i < 0) end_i = 0;
464 if (start_i > end_i) {
465 var tmp = end_i;
466 end_i = start_i;
467 start_i = tmp;
468 }
469 }
470 }
471
472 return %_SubString(s, start_i, end_i);
473}
474
475
476// ES6 draft, revision 26 (2014-07-18), section B.2.3.1
477function StringSubstr(start, n) {
478 CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
479
480 var s = TO_STRING(this);
481 var len;
482
483 // Correct n: If not given, set to string length; if explicitly
484 // set to undefined, zero, or negative, returns empty string.
485 if (IS_UNDEFINED(n)) {
486 len = s.length;
487 } else {
488 len = TO_INTEGER(n);
489 if (len <= 0) return '';
490 }
491
492 // Correct start: If not given (or undefined), set to zero; otherwise
493 // convert to integer and handle negative case.
494 if (IS_UNDEFINED(start)) {
495 start = 0;
496 } else {
497 start = TO_INTEGER(start);
498 // If positive, and greater than or equal to the string length,
499 // return empty string.
500 if (start >= s.length) return '';
501 // If negative and absolute value is larger than the string length,
502 // use zero.
503 if (start < 0) {
504 start += s.length;
505 if (start < 0) start = 0;
506 }
507 }
508
509 var end = start + len;
510 if (end > s.length) end = s.length;
511
512 return %_SubString(s, start, end);
513}
514
515
516// ECMA-262, 15.5.4.16
517function StringToLowerCaseJS() {
518 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
519
520 return %StringToLowerCase(TO_STRING(this));
521}
522
523
524// ECMA-262, 15.5.4.17
525function StringToLocaleLowerCase() {
526 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
527
528 return %StringToLowerCase(TO_STRING(this));
529}
530
531
532// ECMA-262, 15.5.4.18
533function StringToUpperCaseJS() {
534 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
535
536 return %StringToUpperCase(TO_STRING(this));
537}
538
539
540// ECMA-262, 15.5.4.19
541function StringToLocaleUpperCase() {
542 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
543
544 return %StringToUpperCase(TO_STRING(this));
545}
546
547// ES5, 15.5.4.20
548function StringTrimJS() {
549 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
550
551 return %StringTrim(TO_STRING(this), true, true);
552}
553
554function StringTrimLeft() {
555 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
556
557 return %StringTrim(TO_STRING(this), true, false);
558}
559
560function StringTrimRight() {
561 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
562
563 return %StringTrim(TO_STRING(this), false, true);
564}
565
566
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000567// ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
568function HtmlEscape(str) {
569 return %_Call(StringReplace, TO_STRING(str), /"/g, "&quot;");
570}
571
572
573// ES6 draft, revision 26 (2014-07-18), section B.2.3.2
574function StringAnchor(name) {
575 CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor");
576 return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) +
577 "</a>";
578}
579
580
581// ES6 draft, revision 26 (2014-07-18), section B.2.3.3
582function StringBig() {
583 CHECK_OBJECT_COERCIBLE(this, "String.prototype.big");
584 return "<big>" + TO_STRING(this) + "</big>";
585}
586
587
588// ES6 draft, revision 26 (2014-07-18), section B.2.3.4
589function StringBlink() {
590 CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink");
591 return "<blink>" + TO_STRING(this) + "</blink>";
592}
593
594
595// ES6 draft, revision 26 (2014-07-18), section B.2.3.5
596function StringBold() {
597 CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold");
598 return "<b>" + TO_STRING(this) + "</b>";
599}
600
601
602// ES6 draft, revision 26 (2014-07-18), section B.2.3.6
603function StringFixed() {
604 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed");
605 return "<tt>" + TO_STRING(this) + "</tt>";
606}
607
608
609// ES6 draft, revision 26 (2014-07-18), section B.2.3.7
610function StringFontcolor(color) {
611 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor");
612 return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) +
613 "</font>";
614}
615
616
617// ES6 draft, revision 26 (2014-07-18), section B.2.3.8
618function StringFontsize(size) {
619 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize");
620 return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) +
621 "</font>";
622}
623
624
625// ES6 draft, revision 26 (2014-07-18), section B.2.3.9
626function StringItalics() {
627 CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics");
628 return "<i>" + TO_STRING(this) + "</i>";
629}
630
631
632// ES6 draft, revision 26 (2014-07-18), section B.2.3.10
633function StringLink(s) {
634 CHECK_OBJECT_COERCIBLE(this, "String.prototype.link");
635 return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>";
636}
637
638
639// ES6 draft, revision 26 (2014-07-18), section B.2.3.11
640function StringSmall() {
641 CHECK_OBJECT_COERCIBLE(this, "String.prototype.small");
642 return "<small>" + TO_STRING(this) + "</small>";
643}
644
645
646// ES6 draft, revision 26 (2014-07-18), section B.2.3.12
647function StringStrike() {
648 CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike");
649 return "<strike>" + TO_STRING(this) + "</strike>";
650}
651
652
653// ES6 draft, revision 26 (2014-07-18), section B.2.3.13
654function StringSub() {
655 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub");
656 return "<sub>" + TO_STRING(this) + "</sub>";
657}
658
659
660// ES6 draft, revision 26 (2014-07-18), section B.2.3.14
661function StringSup() {
662 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup");
663 return "<sup>" + TO_STRING(this) + "</sup>";
664}
665
666// ES6, section 21.1.3.13
667function StringRepeat(count) {
668 CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat");
669
670 var s = TO_STRING(this);
671 var n = TO_INTEGER(count);
672
673 if (n < 0 || n === INFINITY) throw MakeRangeError(kInvalidCountValue);
674
675 // Early return to allow an arbitrarily-large repeat of the empty string.
676 if (s.length === 0) return "";
677
678 // The maximum string length is stored in a smi, so a longer repeat
679 // must result in a range error.
680 if (n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue);
681
682 var r = "";
683 while (true) {
684 if (n & 1) r += s;
685 n >>= 1;
686 if (n === 0) return r;
687 s += s;
688 }
689}
690
691
692// ES6 draft 04-05-14, section 21.1.3.18
Ben Murdoch097c5b22016-05-18 11:27:45 +0100693function StringStartsWith(searchString, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000694 CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith");
695
696 var s = TO_STRING(this);
697
Ben Murdochda12d292016-06-02 14:46:10 +0100698 if (IsRegExp(searchString)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000699 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith");
700 }
701
702 var ss = TO_STRING(searchString);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100703 var pos = TO_INTEGER(position);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000704
705 var s_len = s.length;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100706 var start = MinSimple(MaxSimple(pos, 0), s_len);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000707 var ss_len = ss.length;
708 if (ss_len + start > s_len) {
709 return false;
710 }
711
712 return %_SubString(s, start, start + ss_len) === ss;
713}
714
Ben Murdoch097c5b22016-05-18 11:27:45 +0100715%FunctionSetLength(StringStartsWith, 1);
716
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000717
718// ES6 draft 04-05-14, section 21.1.3.7
Ben Murdoch097c5b22016-05-18 11:27:45 +0100719function StringEndsWith(searchString, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000720 CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith");
721
722 var s = TO_STRING(this);
723
Ben Murdochda12d292016-06-02 14:46:10 +0100724 if (IsRegExp(searchString)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000725 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith");
726 }
727
728 var ss = TO_STRING(searchString);
729 var s_len = s.length;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100730 var pos = !IS_UNDEFINED(position) ? TO_INTEGER(position) : s_len
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000731
Ben Murdoch097c5b22016-05-18 11:27:45 +0100732 var end = MinSimple(MaxSimple(pos, 0), s_len);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000733 var ss_len = ss.length;
734 var start = end - ss_len;
735 if (start < 0) {
736 return false;
737 }
738
739 return %_SubString(s, start, start + ss_len) === ss;
740}
741
Ben Murdoch097c5b22016-05-18 11:27:45 +0100742%FunctionSetLength(StringEndsWith, 1);
743
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000744
745// ES6 draft 04-05-14, section 21.1.3.6
Ben Murdoch097c5b22016-05-18 11:27:45 +0100746function StringIncludes(searchString, position) { // length == 1
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000747 CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes");
748
749 var string = TO_STRING(this);
750
Ben Murdochda12d292016-06-02 14:46:10 +0100751 if (IsRegExp(searchString)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000752 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes");
753 }
754
755 searchString = TO_STRING(searchString);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100756 var pos = TO_INTEGER(position);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000757
758 var stringLength = string.length;
759 if (pos < 0) pos = 0;
760 if (pos > stringLength) pos = stringLength;
761 var searchStringLength = searchString.length;
762
763 if (searchStringLength + pos > stringLength) {
764 return false;
765 }
766
767 return %StringIndexOf(string, searchString, pos) !== -1;
768}
769
Ben Murdoch097c5b22016-05-18 11:27:45 +0100770%FunctionSetLength(StringIncludes, 1);
771
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000772
773// ES6 Draft 05-22-2014, section 21.1.3.3
774function StringCodePointAt(pos) {
775 CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
776
777 var string = TO_STRING(this);
778 var size = string.length;
779 pos = TO_INTEGER(pos);
780 if (pos < 0 || pos >= size) {
781 return UNDEFINED;
782 }
783 var first = %_StringCharCodeAt(string, pos);
784 if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
785 return first;
786 }
787 var second = %_StringCharCodeAt(string, pos + 1);
788 if (second < 0xDC00 || second > 0xDFFF) {
789 return first;
790 }
791 return (first - 0xD800) * 0x400 + second + 0x2400;
792}
793
794
795// ES6 Draft 05-22-2014, section 21.1.2.2
796function StringFromCodePoint(_) { // length = 1
Ben Murdoch097c5b22016-05-18 11:27:45 +0100797 "use strict";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000798 var code;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100799 var length = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000800 var index;
801 var result = "";
802 for (index = 0; index < length; index++) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100803 code = arguments[index];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000804 if (!%_IsSmi(code)) {
805 code = TO_NUMBER(code);
806 }
807 if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
808 throw MakeRangeError(kInvalidCodePoint, code);
809 }
810 if (code <= 0xFFFF) {
811 result += %_StringCharFromCode(code);
812 } else {
813 code -= 0x10000;
814 result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800);
815 result += %_StringCharFromCode(code & 0x3FF | 0xDC00);
816 }
817 }
818 return result;
819}
820
821
822// -------------------------------------------------------------------
823// String methods related to templates
824
825// ES6 Draft 03-17-2015, section 21.1.2.4
826function StringRaw(callSite) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100827 "use strict";
828 var numberOfSubstitutions = arguments.length;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000829 var cooked = TO_OBJECT(callSite);
830 var raw = TO_OBJECT(cooked.raw);
831 var literalSegments = TO_LENGTH(raw.length);
832 if (literalSegments <= 0) return "";
833
834 var result = TO_STRING(raw[0]);
835
836 for (var i = 1; i < literalSegments; ++i) {
837 if (i < numberOfSubstitutions) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100838 result += TO_STRING(arguments[i]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000839 }
840 result += TO_STRING(raw[i]);
841 }
842
843 return result;
844}
845
846// -------------------------------------------------------------------
847
848// Set the String function and constructor.
849%FunctionSetPrototype(GlobalString, new GlobalString());
850
851// Set up the constructor property on the String prototype object.
852%AddNamedProperty(
853 GlobalString.prototype, "constructor", GlobalString, DONT_ENUM);
854
855// Set up the non-enumerable functions on the String object.
856utils.InstallFunctions(GlobalString, DONT_ENUM, [
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000857 "fromCodePoint", StringFromCodePoint,
858 "raw", StringRaw
859]);
860
861// Set up the non-enumerable functions on the String prototype object.
862utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
863 "valueOf", StringValueOf,
864 "toString", StringToString,
865 "charAt", StringCharAtJS,
866 "charCodeAt", StringCharCodeAtJS,
867 "codePointAt", StringCodePointAt,
868 "concat", StringConcat,
869 "endsWith", StringEndsWith,
870 "includes", StringIncludes,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100871 "indexOf", StringIndexOf,
872 "lastIndexOf", StringLastIndexOf,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000873 "localeCompare", StringLocaleCompareJS,
874 "match", StringMatchJS,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100875 "normalize", StringNormalize,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000876 "repeat", StringRepeat,
877 "replace", StringReplace,
878 "search", StringSearch,
879 "slice", StringSlice,
880 "split", StringSplitJS,
881 "substring", StringSubstring,
882 "substr", StringSubstr,
883 "startsWith", StringStartsWith,
884 "toLowerCase", StringToLowerCaseJS,
885 "toLocaleLowerCase", StringToLocaleLowerCase,
886 "toUpperCase", StringToUpperCaseJS,
887 "toLocaleUpperCase", StringToLocaleUpperCase,
888 "trim", StringTrimJS,
889 "trimLeft", StringTrimLeft,
890 "trimRight", StringTrimRight,
891
892 "link", StringLink,
893 "anchor", StringAnchor,
894 "fontcolor", StringFontcolor,
895 "fontsize", StringFontsize,
896 "big", StringBig,
897 "blink", StringBlink,
898 "bold", StringBold,
899 "fixed", StringFixed,
900 "italics", StringItalics,
901 "small", StringSmall,
902 "strike", StringStrike,
903 "sub", StringSub,
904 "sup", StringSup
905]);
906
907// -------------------------------------------------------------------
908// Exports
909
910utils.Export(function(to) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100911 to.ExpandReplacement = ExpandReplacement;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000912 to.StringCharAt = StringCharAtJS;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100913 to.StringIndexOf = StringIndexOf;
914 to.StringLastIndexOf = StringLastIndexOf;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000915 to.StringMatch = StringMatchJS;
916 to.StringReplace = StringReplace;
917 to.StringSlice = StringSlice;
918 to.StringSplit = StringSplitJS;
919 to.StringSubstr = StringSubstr;
920 to.StringSubstring = StringSubstring;
921});
922
923})