Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: src/string.js

Issue 1398733002: Move builtin JavaScript sources into own directory. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Also move macros.py file. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/runtime.js ('k') | src/string-iterator.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 (function(global, utils) {
6
7 %CheckIsBootstrapping();
8
9 // -------------------------------------------------------------------
10 // Imports
11
12 var ArrayIndexOf;
13 var ArrayJoin;
14 var GlobalRegExp = global.RegExp;
15 var GlobalString = global.String;
16 var InternalArray = utils.InternalArray;
17 var InternalPackedArray = utils.InternalPackedArray;
18 var RegExpExec;
19 var RegExpExecNoTests;
20 var RegExpLastMatchInfo;
21
22 utils.Import(function(from) {
23 ArrayIndexOf = from.ArrayIndexOf;
24 ArrayJoin = from.ArrayJoin;
25 RegExpExec = from.RegExpExec;
26 RegExpExecNoTests = from.RegExpExecNoTests;
27 RegExpLastMatchInfo = from.RegExpLastMatchInfo;
28 });
29
30 //-------------------------------------------------------------------
31
32 // ECMA-262 section 15.5.4.2
33 function StringToString() {
34 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
35 throw MakeTypeError(kNotGeneric, 'String.prototype.toString');
36 }
37 return %_ValueOf(this);
38 }
39
40
41 // ECMA-262 section 15.5.4.3
42 function StringValueOf() {
43 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
44 throw MakeTypeError(kNotGeneric, 'String.prototype.valueOf');
45 }
46 return %_ValueOf(this);
47 }
48
49
50 // ECMA-262, section 15.5.4.4
51 function StringCharAtJS(pos) {
52 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
53
54 var result = %_StringCharAt(this, pos);
55 if (%_IsSmi(result)) {
56 result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos));
57 }
58 return result;
59 }
60
61
62 // ECMA-262 section 15.5.4.5
63 function StringCharCodeAtJS(pos) {
64 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
65
66 var result = %_StringCharCodeAt(this, pos);
67 if (!%_IsSmi(result)) {
68 result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos));
69 }
70 return result;
71 }
72
73
74 // ECMA-262, section 15.5.4.6
75 function StringConcat(other /* and more */) { // length == 1
76 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
77 var len = %_ArgumentsLength();
78 var this_as_string = TO_STRING(this);
79 if (len === 1) {
80 return this_as_string + TO_STRING(other);
81 }
82 var parts = new InternalArray(len + 1);
83 parts[0] = this_as_string;
84 for (var i = 0; i < len; i++) {
85 var part = %_Arguments(i);
86 parts[i + 1] = TO_STRING(part);
87 }
88 return %StringBuilderConcat(parts, len + 1, "");
89 }
90
91
92 // ECMA-262 section 15.5.4.7
93 function StringIndexOfJS(pattern /* position */) { // length == 1
94 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
95
96 var subject = TO_STRING(this);
97 pattern = TO_STRING(pattern);
98 var index = 0;
99 if (%_ArgumentsLength() > 1) {
100 index = %_Arguments(1); // position
101 index = TO_INTEGER(index);
102 if (index < 0) index = 0;
103 if (index > subject.length) index = subject.length;
104 }
105 return %StringIndexOf(subject, pattern, index);
106 }
107
108
109 // ECMA-262 section 15.5.4.8
110 function StringLastIndexOfJS(pat /* position */) { // length == 1
111 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
112
113 var sub = TO_STRING(this);
114 var subLength = sub.length;
115 var pat = TO_STRING(pat);
116 var patLength = pat.length;
117 var index = subLength - patLength;
118 if (%_ArgumentsLength() > 1) {
119 var position = TO_NUMBER(%_Arguments(1));
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;
127 }
128 }
129 }
130 if (index < 0) {
131 return -1;
132 }
133 return %StringLastIndexOf(sub, pat, index);
134 }
135
136
137 // ECMA-262 section 15.5.4.9
138 //
139 // This function is implementation specific. For now, we do not
140 // do anything locale specific.
141 function StringLocaleCompareJS(other) {
142 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
143
144 return %StringLocaleCompare(TO_STRING(this), TO_STRING(other));
145 }
146
147
148 // ECMA-262 section 15.5.4.10
149 function StringMatchJS(regexp) {
150 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
151
152 var subject = TO_STRING(this);
153 if (IS_REGEXP(regexp)) {
154 // Emulate RegExp.prototype.exec's side effect in step 5, even though
155 // value is discarded.
156 var lastIndex = TO_INTEGER(regexp.lastIndex);
157 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
158 var result = %StringMatch(subject, regexp, RegExpLastMatchInfo);
159 if (result !== null) $regexpLastMatchInfoOverride = null;
160 regexp.lastIndex = 0;
161 return result;
162 }
163 // Non-regexp argument.
164 regexp = new GlobalRegExp(regexp);
165 return RegExpExecNoTests(regexp, subject, 0);
166 }
167
168
169 // ECMA-262 v6, section 21.1.3.12
170 //
171 // For now we do nothing, as proper normalization requires big tables.
172 // If Intl is enabled, then i18n.js will override it and provide the the
173 // proper functionality.
174 function StringNormalizeJS() {
175 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
176 var s = TO_STRING(this);
177
178 var formArg = %_Arguments(0);
179 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
180
181 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
182 var normalizationForm =
183 %_CallFunction(NORMALIZATION_FORMS, form, ArrayIndexOf);
184 if (normalizationForm === -1) {
185 throw MakeRangeError(kNormalizationForm,
186 %_CallFunction(NORMALIZATION_FORMS, ', ', ArrayJoin));
187 }
188
189 return s;
190 }
191
192
193 // This has the same size as the RegExpLastMatchInfo array, and can be used
194 // for functions that expect that structure to be returned. It is used when
195 // the needle is a string rather than a regexp. In this case we can't update
196 // lastMatchArray without erroneously affecting the properties on the global
197 // RegExp object.
198 var reusableMatchInfo = [2, "", "", -1, -1];
199
200
201 // ECMA-262, section 15.5.4.11
202 function StringReplace(search, replace) {
203 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
204
205 var subject = TO_STRING(this);
206
207 // Decision tree for dispatch
208 // .. regexp search
209 // .... 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
224 if (IS_REGEXP(search)) {
225 // Emulate RegExp.prototype.exec's side effect in step 5, even if
226 // value is discarded.
227 var lastIndex = TO_INTEGER(search.lastIndex);
228
229 if (!IS_CALLABLE(replace)) {
230 replace = TO_STRING(replace);
231
232 if (!search.global) {
233 // Non-global regexp search, string replace.
234 var match = RegExpExec(search, subject, 0);
235 if (match == null) {
236 search.lastIndex = 0
237 return subject;
238 }
239 if (replace.length == 0) {
240 return %_SubString(subject, 0, match[CAPTURE0]) +
241 %_SubString(subject, match[CAPTURE1], subject.length)
242 }
243 return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
244 %_SubString(subject, 0, match[CAPTURE0])) +
245 %_SubString(subject, match[CAPTURE1], subject.length);
246 }
247
248 // Global regexp search, string replace.
249 search.lastIndex = 0;
250 if ($regexpLastMatchInfoOverride == null) {
251 return %StringReplaceGlobalRegExpWithString(
252 subject, search, replace, RegExpLastMatchInfo);
253 } else {
254 // We use this hack to detect whether StringReplaceRegExpWithString
255 // found at least one hit. In that case we need to remove any
256 // override.
257 var saved_subject = RegExpLastMatchInfo[LAST_SUBJECT_INDEX];
258 RegExpLastMatchInfo[LAST_SUBJECT_INDEX] = 0;
259 var answer = %StringReplaceGlobalRegExpWithString(
260 subject, search, replace, RegExpLastMatchInfo);
261 if (%_IsSmi(RegExpLastMatchInfo[LAST_SUBJECT_INDEX])) {
262 RegExpLastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
263 } else {
264 $regexpLastMatchInfoOverride = null;
265 }
266 return answer;
267 }
268 }
269
270 if (search.global) {
271 // Global regexp search, function replace.
272 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
273 }
274 // Non-global regexp search, function replace.
275 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
276 }
277
278 search = TO_STRING(search);
279
280 if (search.length == 1 &&
281 subject.length > 0xFF &&
282 IS_STRING(replace) &&
283 %StringIndexOf(replace, '$', 0) < 0) {
284 // Searching by traversing a cons string tree and replace with cons of
285 // slices works only when the replaced string is a single character, being
286 // replaced by a simple string and only pays off for long strings.
287 return %StringReplaceOneCharWithString(subject, search, replace);
288 }
289 var start = %StringIndexOf(subject, search, 0);
290 if (start < 0) return subject;
291 var end = start + search.length;
292
293 var result = %_SubString(subject, 0, start);
294
295 // Compute the string to replace with.
296 if (IS_CALLABLE(replace)) {
297 result += replace(search, start, subject);
298 } else {
299 reusableMatchInfo[CAPTURE0] = start;
300 reusableMatchInfo[CAPTURE1] = end;
301 result = ExpandReplacement(TO_STRING(replace),
302 subject,
303 reusableMatchInfo,
304 result);
305 }
306
307 return result + %_SubString(subject, end, subject.length);
308 }
309
310
311 // Expand the $-expressions in the string and return a new string with
312 // the result.
313 function ExpandReplacement(string, subject, matchInfo, result) {
314 var length = string.length;
315 var next = %StringIndexOf(string, '$', 0);
316 if (next < 0) {
317 if (length > 0) result += string;
318 return result;
319 }
320
321 if (next > 0) result += %_SubString(string, 0, next);
322
323 while (true) {
324 var expansion = '$';
325 var position = next + 1;
326 if (position < length) {
327 var peek = %_StringCharCodeAt(string, position);
328 if (peek == 36) { // $$
329 ++position;
330 result += '$';
331 } else if (peek == 38) { // $& - match
332 ++position;
333 result +=
334 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
335 } else if (peek == 96) { // $` - prefix
336 ++position;
337 result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
338 } else if (peek == 39) { // $' - suffix
339 ++position;
340 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
341 } else if (peek >= 48 && peek <= 57) {
342 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
343 var scaled_index = (peek - 48) << 1;
344 var advance = 1;
345 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
346 if (position + 1 < string.length) {
347 var next = %_StringCharCodeAt(string, position + 1);
348 if (next >= 48 && next <= 57) {
349 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
350 if (new_scaled_index < number_of_captures) {
351 scaled_index = new_scaled_index;
352 advance = 2;
353 }
354 }
355 }
356 if (scaled_index != 0 && scaled_index < number_of_captures) {
357 var start = matchInfo[CAPTURE(scaled_index)];
358 if (start >= 0) {
359 result +=
360 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
361 }
362 position += advance;
363 } else {
364 result += '$';
365 }
366 } else {
367 result += '$';
368 }
369 } else {
370 result += '$';
371 }
372
373 // Go the the next $ in the string.
374 next = %StringIndexOf(string, '$', position);
375
376 // Return if there are no more $ characters in the string. If we
377 // haven't reached the end, we need to append the suffix.
378 if (next < 0) {
379 if (position < length) {
380 result += %_SubString(string, position, length);
381 }
382 return result;
383 }
384
385 // Append substring between the previous and the next $ character.
386 if (next > position) {
387 result += %_SubString(string, position, next);
388 }
389 }
390 return result;
391 }
392
393
394 // Compute the string of a given regular expression capture.
395 function CaptureString(string, lastCaptureInfo, index) {
396 // Scale the index.
397 var scaled = index << 1;
398 // Compute start and end.
399 var start = lastCaptureInfo[CAPTURE(scaled)];
400 // If start isn't valid, return undefined.
401 if (start < 0) return;
402 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
403 return %_SubString(string, start, end);
404 }
405
406
407 // TODO(lrn): This array will survive indefinitely if replace is never
408 // called again. However, it will be empty, since the contents are cleared
409 // in the finally block.
410 var reusableReplaceArray = new InternalArray(4);
411
412 // Helper function for replacing regular expressions with the result of a
413 // function application in String.prototype.replace.
414 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
415 var resultArray = reusableReplaceArray;
416 if (resultArray) {
417 reusableReplaceArray = null;
418 } else {
419 // Inside a nested replace (replace called from the replacement function
420 // of another replace) or we have failed to set the reusable array
421 // back due to an exception in a replacement function. Create a new
422 // array to use in the future, or until the original is written back.
423 resultArray = new InternalArray(16);
424 }
425 var res = %RegExpExecMultiple(regexp,
426 subject,
427 RegExpLastMatchInfo,
428 resultArray);
429 regexp.lastIndex = 0;
430 if (IS_NULL(res)) {
431 // No matches at all.
432 reusableReplaceArray = resultArray;
433 return subject;
434 }
435 var len = res.length;
436 if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) {
437 // If the number of captures is two then there are no explicit captures in
438 // the regexp, just the implicit capture that captures the whole match. In
439 // this case we can simplify quite a bit and end up with something faster.
440 // The builder will consist of some integers that indicate slices of the
441 // input string and some replacements that were returned from the replace
442 // function.
443 var match_start = 0;
444 var override = new InternalPackedArray(null, 0, subject);
445 for (var i = 0; i < len; i++) {
446 var elem = res[i];
447 if (%_IsSmi(elem)) {
448 // Integers represent slices of the original string. Use these to
449 // get the offsets we need for the override array (so things like
450 // RegExp.leftContext work during the callback function.
451 if (elem > 0) {
452 match_start = (elem >> 11) + (elem & 0x7ff);
453 } else {
454 match_start = res[++i] - elem;
455 }
456 } else {
457 override[0] = elem;
458 override[1] = match_start;
459 $regexpLastMatchInfoOverride = override;
460 var func_result = replace(elem, match_start, subject);
461 // Overwrite the i'th element in the results with the string we got
462 // back from the callback function.
463 res[i] = TO_STRING(func_result);
464 match_start += elem.length;
465 }
466 }
467 } else {
468 for (var i = 0; i < len; i++) {
469 var elem = res[i];
470 if (!%_IsSmi(elem)) {
471 // elem must be an Array.
472 // Use the apply argument as backing for global RegExp properties.
473 $regexpLastMatchInfoOverride = elem;
474 var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length);
475 // Overwrite the i'th element in the results with the string we got
476 // back from the callback function.
477 res[i] = TO_STRING(func_result);
478 }
479 }
480 }
481 var result = %StringBuilderConcat(res, res.length, subject);
482 resultArray.length = 0;
483 reusableReplaceArray = resultArray;
484 return result;
485 }
486
487
488 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
489 var matchInfo = RegExpExec(regexp, subject, 0);
490 if (IS_NULL(matchInfo)) {
491 regexp.lastIndex = 0;
492 return subject;
493 }
494 var index = matchInfo[CAPTURE0];
495 var result = %_SubString(subject, 0, index);
496 var endOfMatch = matchInfo[CAPTURE1];
497 // Compute the parameter list consisting of the match, captures, index,
498 // and subject for the replace function invocation.
499 // The number of captures plus one for the match.
500 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
501 var replacement;
502 if (m == 1) {
503 // No captures, only the match, which is always valid.
504 var s = %_SubString(subject, index, endOfMatch);
505 // Don't call directly to avoid exposing the built-in global object.
506 replacement = replace(s, index, subject);
507 } else {
508 var parameters = new InternalArray(m + 2);
509 for (var j = 0; j < m; j++) {
510 parameters[j] = CaptureString(subject, matchInfo, j);
511 }
512 parameters[j] = index;
513 parameters[j + 1] = subject;
514
515 replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2);
516 }
517
518 result += replacement; // The add method converts to string if necessary.
519 // Can't use matchInfo any more from here, since the function could
520 // overwrite it.
521 return result + %_SubString(subject, endOfMatch, subject.length);
522 }
523
524
525 // ECMA-262 section 15.5.4.12
526 function StringSearch(re) {
527 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
528
529 var regexp;
530 if (IS_REGEXP(re)) {
531 regexp = re;
532 } else {
533 regexp = new GlobalRegExp(re);
534 }
535 var match = RegExpExec(regexp, TO_STRING(this), 0);
536 if (match) {
537 return match[CAPTURE0];
538 }
539 return -1;
540 }
541
542
543 // ECMA-262 section 15.5.4.13
544 function StringSlice(start, end) {
545 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
546
547 var s = TO_STRING(this);
548 var s_len = s.length;
549 var start_i = TO_INTEGER(start);
550 var end_i = s_len;
551 if (!IS_UNDEFINED(end)) {
552 end_i = TO_INTEGER(end);
553 }
554
555 if (start_i < 0) {
556 start_i += s_len;
557 if (start_i < 0) {
558 start_i = 0;
559 }
560 } else {
561 if (start_i > s_len) {
562 return '';
563 }
564 }
565
566 if (end_i < 0) {
567 end_i += s_len;
568 if (end_i < 0) {
569 return '';
570 }
571 } else {
572 if (end_i > s_len) {
573 end_i = s_len;
574 }
575 }
576
577 if (end_i <= start_i) {
578 return '';
579 }
580
581 return %_SubString(s, start_i, end_i);
582 }
583
584
585 // ECMA-262 section 15.5.4.14
586 function StringSplitJS(separator, limit) {
587 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
588
589 var subject = TO_STRING(this);
590 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
591
592 var length = subject.length;
593 if (!IS_REGEXP(separator)) {
594 var separator_string = TO_STRING(separator);
595
596 if (limit === 0) return [];
597
598 // ECMA-262 says that if separator is undefined, the result should
599 // be an array of size 1 containing the entire string.
600 if (IS_UNDEFINED(separator)) return [subject];
601
602 var separator_length = separator_string.length;
603
604 // If the separator string is empty then return the elements in the subject.
605 if (separator_length === 0) return %StringToArray(subject, limit);
606
607 var result = %StringSplit(subject, separator_string, limit);
608
609 return result;
610 }
611
612 if (limit === 0) return [];
613
614 // Separator is a regular expression.
615 return StringSplitOnRegExp(subject, separator, limit, length);
616 }
617
618
619 function StringSplitOnRegExp(subject, separator, limit, length) {
620 if (length === 0) {
621 if (RegExpExec(separator, subject, 0, 0) != null) {
622 return [];
623 }
624 return [subject];
625 }
626
627 var currentIndex = 0;
628 var startIndex = 0;
629 var startMatch = 0;
630 var result = new InternalArray();
631
632 outer_loop:
633 while (true) {
634
635 if (startIndex === length) {
636 result[result.length] = %_SubString(subject, currentIndex, length);
637 break;
638 }
639
640 var matchInfo = RegExpExec(separator, subject, startIndex);
641 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
642 result[result.length] = %_SubString(subject, currentIndex, length);
643 break;
644 }
645 var endIndex = matchInfo[CAPTURE1];
646
647 // We ignore a zero-length match at the currentIndex.
648 if (startIndex === endIndex && endIndex === currentIndex) {
649 startIndex++;
650 continue;
651 }
652
653 result[result.length] = %_SubString(subject, currentIndex, startMatch);
654
655 if (result.length === limit) break;
656
657 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
658 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
659 var start = matchInfo[i++];
660 var end = matchInfo[i++];
661 if (end != -1) {
662 result[result.length] = %_SubString(subject, start, end);
663 } else {
664 result[result.length] = UNDEFINED;
665 }
666 if (result.length === limit) break outer_loop;
667 }
668
669 startIndex = currentIndex = endIndex;
670 }
671 var array_result = [];
672 %MoveArrayContents(result, array_result);
673 return array_result;
674 }
675
676
677 // ECMA-262 section 15.5.4.15
678 function StringSubstring(start, end) {
679 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
680
681 var s = TO_STRING(this);
682 var s_len = s.length;
683
684 var start_i = TO_INTEGER(start);
685 if (start_i < 0) {
686 start_i = 0;
687 } else if (start_i > s_len) {
688 start_i = s_len;
689 }
690
691 var end_i = s_len;
692 if (!IS_UNDEFINED(end)) {
693 end_i = TO_INTEGER(end);
694 if (end_i > s_len) {
695 end_i = s_len;
696 } else {
697 if (end_i < 0) end_i = 0;
698 if (start_i > end_i) {
699 var tmp = end_i;
700 end_i = start_i;
701 start_i = tmp;
702 }
703 }
704 }
705
706 return %_SubString(s, start_i, end_i);
707 }
708
709
710 // ES6 draft, revision 26 (2014-07-18), section B.2.3.1
711 function StringSubstr(start, n) {
712 CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
713
714 var s = TO_STRING(this);
715 var len;
716
717 // Correct n: If not given, set to string length; if explicitly
718 // set to undefined, zero, or negative, returns empty string.
719 if (IS_UNDEFINED(n)) {
720 len = s.length;
721 } else {
722 len = TO_INTEGER(n);
723 if (len <= 0) return '';
724 }
725
726 // Correct start: If not given (or undefined), set to zero; otherwise
727 // convert to integer and handle negative case.
728 if (IS_UNDEFINED(start)) {
729 start = 0;
730 } else {
731 start = TO_INTEGER(start);
732 // If positive, and greater than or equal to the string length,
733 // return empty string.
734 if (start >= s.length) return '';
735 // If negative and absolute value is larger than the string length,
736 // use zero.
737 if (start < 0) {
738 start += s.length;
739 if (start < 0) start = 0;
740 }
741 }
742
743 var end = start + len;
744 if (end > s.length) end = s.length;
745
746 return %_SubString(s, start, end);
747 }
748
749
750 // ECMA-262, 15.5.4.16
751 function StringToLowerCaseJS() {
752 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
753
754 return %StringToLowerCase(TO_STRING(this));
755 }
756
757
758 // ECMA-262, 15.5.4.17
759 function StringToLocaleLowerCase() {
760 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
761
762 return %StringToLowerCase(TO_STRING(this));
763 }
764
765
766 // ECMA-262, 15.5.4.18
767 function StringToUpperCaseJS() {
768 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
769
770 return %StringToUpperCase(TO_STRING(this));
771 }
772
773
774 // ECMA-262, 15.5.4.19
775 function StringToLocaleUpperCase() {
776 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
777
778 return %StringToUpperCase(TO_STRING(this));
779 }
780
781 // ES5, 15.5.4.20
782 function StringTrimJS() {
783 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
784
785 return %StringTrim(TO_STRING(this), true, true);
786 }
787
788 function StringTrimLeft() {
789 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
790
791 return %StringTrim(TO_STRING(this), true, false);
792 }
793
794 function StringTrimRight() {
795 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
796
797 return %StringTrim(TO_STRING(this), false, true);
798 }
799
800
801 // ECMA-262, section 15.5.3.2
802 function StringFromCharCode(code) {
803 var n = %_ArgumentsLength();
804 if (n == 1) {
805 if (!%_IsSmi(code)) code = TO_NUMBER(code);
806 return %_StringCharFromCode(code & 0xffff);
807 }
808
809 var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
810 var i;
811 for (i = 0; i < n; i++) {
812 var code = %_Arguments(i);
813 if (!%_IsSmi(code)) code = TO_NUMBER(code) & 0xffff;
814 if (code < 0) code = code & 0xffff;
815 if (code > 0xff) break;
816 %_OneByteSeqStringSetChar(i, code, one_byte);
817 }
818 if (i == n) return one_byte;
819 one_byte = %TruncateString(one_byte, i);
820
821 var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING);
822 for (var j = 0; i < n; i++, j++) {
823 var code = %_Arguments(i);
824 if (!%_IsSmi(code)) code = TO_NUMBER(code) & 0xffff;
825 %_TwoByteSeqStringSetChar(j, code, two_byte);
826 }
827 return one_byte + two_byte;
828 }
829
830
831 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
832 function HtmlEscape(str) {
833 return %_CallFunction(TO_STRING(str), /"/g, "&quot;", StringReplace);
834 }
835
836
837 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2
838 function StringAnchor(name) {
839 CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor");
840 return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) +
841 "</a>";
842 }
843
844
845 // ES6 draft, revision 26 (2014-07-18), section B.2.3.3
846 function StringBig() {
847 CHECK_OBJECT_COERCIBLE(this, "String.prototype.big");
848 return "<big>" + TO_STRING(this) + "</big>";
849 }
850
851
852 // ES6 draft, revision 26 (2014-07-18), section B.2.3.4
853 function StringBlink() {
854 CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink");
855 return "<blink>" + TO_STRING(this) + "</blink>";
856 }
857
858
859 // ES6 draft, revision 26 (2014-07-18), section B.2.3.5
860 function StringBold() {
861 CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold");
862 return "<b>" + TO_STRING(this) + "</b>";
863 }
864
865
866 // ES6 draft, revision 26 (2014-07-18), section B.2.3.6
867 function StringFixed() {
868 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed");
869 return "<tt>" + TO_STRING(this) + "</tt>";
870 }
871
872
873 // ES6 draft, revision 26 (2014-07-18), section B.2.3.7
874 function StringFontcolor(color) {
875 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor");
876 return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) +
877 "</font>";
878 }
879
880
881 // ES6 draft, revision 26 (2014-07-18), section B.2.3.8
882 function StringFontsize(size) {
883 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize");
884 return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) +
885 "</font>";
886 }
887
888
889 // ES6 draft, revision 26 (2014-07-18), section B.2.3.9
890 function StringItalics() {
891 CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics");
892 return "<i>" + TO_STRING(this) + "</i>";
893 }
894
895
896 // ES6 draft, revision 26 (2014-07-18), section B.2.3.10
897 function StringLink(s) {
898 CHECK_OBJECT_COERCIBLE(this, "String.prototype.link");
899 return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>";
900 }
901
902
903 // ES6 draft, revision 26 (2014-07-18), section B.2.3.11
904 function StringSmall() {
905 CHECK_OBJECT_COERCIBLE(this, "String.prototype.small");
906 return "<small>" + TO_STRING(this) + "</small>";
907 }
908
909
910 // ES6 draft, revision 26 (2014-07-18), section B.2.3.12
911 function StringStrike() {
912 CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike");
913 return "<strike>" + TO_STRING(this) + "</strike>";
914 }
915
916
917 // ES6 draft, revision 26 (2014-07-18), section B.2.3.13
918 function StringSub() {
919 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub");
920 return "<sub>" + TO_STRING(this) + "</sub>";
921 }
922
923
924 // ES6 draft, revision 26 (2014-07-18), section B.2.3.14
925 function StringSup() {
926 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup");
927 return "<sup>" + TO_STRING(this) + "</sup>";
928 }
929
930 // ES6 draft 01-20-14, section 21.1.3.13
931 function StringRepeat(count) {
932 CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat");
933
934 var s = TO_STRING(this);
935 var n = TO_INTEGER(count);
936 // The maximum string length is stored in a smi, so a longer repeat
937 // must result in a range error.
938 if (n < 0 || n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue);
939
940 var r = "";
941 while (true) {
942 if (n & 1) r += s;
943 n >>= 1;
944 if (n === 0) return r;
945 s += s;
946 }
947 }
948
949
950 // ES6 draft 04-05-14, section 21.1.3.18
951 function StringStartsWith(searchString /* position */) { // length == 1
952 CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith");
953
954 var s = TO_STRING(this);
955
956 if (IS_REGEXP(searchString)) {
957 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith");
958 }
959
960 var ss = TO_STRING(searchString);
961 var pos = 0;
962 if (%_ArgumentsLength() > 1) {
963 var arg = %_Arguments(1); // position
964 if (!IS_UNDEFINED(arg)) {
965 pos = TO_INTEGER(arg);
966 }
967 }
968
969 var s_len = s.length;
970 if (pos < 0) pos = 0;
971 if (pos > s_len) pos = s_len;
972 var ss_len = ss.length;
973
974 if (ss_len + pos > s_len) {
975 return false;
976 }
977
978 for (var i = 0; i < ss_len; i++) {
979 if (%_StringCharCodeAt(s, pos + i) !== %_StringCharCodeAt(ss, i)) {
980 return false;
981 }
982 }
983
984 return true;
985 }
986
987
988 // ES6 draft 04-05-14, section 21.1.3.7
989 function StringEndsWith(searchString /* position */) { // length == 1
990 CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith");
991
992 var s = TO_STRING(this);
993
994 if (IS_REGEXP(searchString)) {
995 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith");
996 }
997
998 var ss = TO_STRING(searchString);
999 var s_len = s.length;
1000 var pos = s_len;
1001 if (%_ArgumentsLength() > 1) {
1002 var arg = %_Arguments(1); // position
1003 if (!IS_UNDEFINED(arg)) {
1004 pos = TO_INTEGER(arg);
1005 }
1006 }
1007
1008 if (pos < 0) pos = 0;
1009 if (pos > s_len) pos = s_len;
1010 var ss_len = ss.length;
1011 pos = pos - ss_len;
1012
1013 if (pos < 0) {
1014 return false;
1015 }
1016
1017 for (var i = 0; i < ss_len; i++) {
1018 if (%_StringCharCodeAt(s, pos + i) !== %_StringCharCodeAt(ss, i)) {
1019 return false;
1020 }
1021 }
1022
1023 return true;
1024 }
1025
1026
1027 // ES6 draft 04-05-14, section 21.1.3.6
1028 function StringIncludes(searchString /* position */) { // length == 1
1029 CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes");
1030
1031 var string = TO_STRING(this);
1032
1033 if (IS_REGEXP(searchString)) {
1034 throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes");
1035 }
1036
1037 searchString = TO_STRING(searchString);
1038 var pos = 0;
1039 if (%_ArgumentsLength() > 1) {
1040 pos = %_Arguments(1); // position
1041 pos = TO_INTEGER(pos);
1042 }
1043
1044 var stringLength = string.length;
1045 if (pos < 0) pos = 0;
1046 if (pos > stringLength) pos = stringLength;
1047 var searchStringLength = searchString.length;
1048
1049 if (searchStringLength + pos > stringLength) {
1050 return false;
1051 }
1052
1053 return %StringIndexOf(string, searchString, pos) !== -1;
1054 }
1055
1056
1057 // ES6 Draft 05-22-2014, section 21.1.3.3
1058 function StringCodePointAt(pos) {
1059 CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
1060
1061 var string = TO_STRING(this);
1062 var size = string.length;
1063 pos = TO_INTEGER(pos);
1064 if (pos < 0 || pos >= size) {
1065 return UNDEFINED;
1066 }
1067 var first = %_StringCharCodeAt(string, pos);
1068 if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
1069 return first;
1070 }
1071 var second = %_StringCharCodeAt(string, pos + 1);
1072 if (second < 0xDC00 || second > 0xDFFF) {
1073 return first;
1074 }
1075 return (first - 0xD800) * 0x400 + second + 0x2400;
1076 }
1077
1078
1079 // ES6 Draft 05-22-2014, section 21.1.2.2
1080 function StringFromCodePoint(_) { // length = 1
1081 var code;
1082 var length = %_ArgumentsLength();
1083 var index;
1084 var result = "";
1085 for (index = 0; index < length; index++) {
1086 code = %_Arguments(index);
1087 if (!%_IsSmi(code)) {
1088 code = TO_NUMBER(code);
1089 }
1090 if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
1091 throw MakeRangeError(kInvalidCodePoint, code);
1092 }
1093 if (code <= 0xFFFF) {
1094 result += %_StringCharFromCode(code);
1095 } else {
1096 code -= 0x10000;
1097 result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800);
1098 result += %_StringCharFromCode(code & 0x3FF | 0xDC00);
1099 }
1100 }
1101 return result;
1102 }
1103
1104
1105 // -------------------------------------------------------------------
1106 // String methods related to templates
1107
1108 // ES6 Draft 03-17-2015, section 21.1.2.4
1109 function StringRaw(callSite) {
1110 // TODO(caitp): Use rest parameters when implemented
1111 var numberOfSubstitutions = %_ArgumentsLength();
1112 var cooked = TO_OBJECT(callSite);
1113 var raw = TO_OBJECT(cooked.raw);
1114 var literalSegments = TO_LENGTH(raw.length);
1115 if (literalSegments <= 0) return "";
1116
1117 var result = TO_STRING(raw[0]);
1118
1119 for (var i = 1; i < literalSegments; ++i) {
1120 if (i < numberOfSubstitutions) {
1121 result += TO_STRING(%_Arguments(i));
1122 }
1123 result += TO_STRING(raw[i]);
1124 }
1125
1126 return result;
1127 }
1128
1129 // -------------------------------------------------------------------
1130
1131 // Set the String function and constructor.
1132 %FunctionSetPrototype(GlobalString, new GlobalString());
1133
1134 // Set up the constructor property on the String prototype object.
1135 %AddNamedProperty(
1136 GlobalString.prototype, "constructor", GlobalString, DONT_ENUM);
1137
1138 // Set up the non-enumerable functions on the String object.
1139 utils.InstallFunctions(GlobalString, DONT_ENUM, [
1140 "fromCharCode", StringFromCharCode,
1141 "fromCodePoint", StringFromCodePoint,
1142 "raw", StringRaw
1143 ]);
1144
1145 // Set up the non-enumerable functions on the String prototype object.
1146 utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
1147 "valueOf", StringValueOf,
1148 "toString", StringToString,
1149 "charAt", StringCharAtJS,
1150 "charCodeAt", StringCharCodeAtJS,
1151 "codePointAt", StringCodePointAt,
1152 "concat", StringConcat,
1153 "endsWith", StringEndsWith,
1154 "includes", StringIncludes,
1155 "indexOf", StringIndexOfJS,
1156 "lastIndexOf", StringLastIndexOfJS,
1157 "localeCompare", StringLocaleCompareJS,
1158 "match", StringMatchJS,
1159 "normalize", StringNormalizeJS,
1160 "repeat", StringRepeat,
1161 "replace", StringReplace,
1162 "search", StringSearch,
1163 "slice", StringSlice,
1164 "split", StringSplitJS,
1165 "substring", StringSubstring,
1166 "substr", StringSubstr,
1167 "startsWith", StringStartsWith,
1168 "toLowerCase", StringToLowerCaseJS,
1169 "toLocaleLowerCase", StringToLocaleLowerCase,
1170 "toUpperCase", StringToUpperCaseJS,
1171 "toLocaleUpperCase", StringToLocaleUpperCase,
1172 "trim", StringTrimJS,
1173 "trimLeft", StringTrimLeft,
1174 "trimRight", StringTrimRight,
1175
1176 "link", StringLink,
1177 "anchor", StringAnchor,
1178 "fontcolor", StringFontcolor,
1179 "fontsize", StringFontsize,
1180 "big", StringBig,
1181 "blink", StringBlink,
1182 "bold", StringBold,
1183 "fixed", StringFixed,
1184 "italics", StringItalics,
1185 "small", StringSmall,
1186 "strike", StringStrike,
1187 "sub", StringSub,
1188 "sup", StringSup
1189 ]);
1190
1191 // -------------------------------------------------------------------
1192 // Exports
1193
1194 utils.Export(function(to) {
1195 to.StringCharAt = StringCharAtJS;
1196 to.StringIndexOf = StringIndexOfJS;
1197 to.StringLastIndexOf = StringLastIndexOfJS;
1198 to.StringMatch = StringMatchJS;
1199 to.StringReplace = StringReplace;
1200 to.StringSlice = StringSlice;
1201 to.StringSplit = StringSplitJS;
1202 to.StringSubstr = StringSubstr;
1203 to.StringSubstring = StringSubstring;
1204 });
1205
1206 })
OLDNEW
« no previous file with comments | « src/runtime.js ('k') | src/string-iterator.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698