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