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.
5 // This file relies on the fact that the following declaration has been made
7 // var $String = global.String;
9 // -------------------------------------------------------------------
11 function StringConstructor(x) {
12 if (%_ArgumentsLength() == 0) x = '';
13 if (%_IsConstructCall()) {
14 %_SetValueOf(this, TO_STRING_INLINE(x));
17 %_CallFunction(x, SymbolToString) : TO_STRING_INLINE(x);
22 // ECMA-262 section 15.5.4.2
23 function StringToString() {
24 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
25 throw new $TypeError('String.prototype.toString is not generic');
27 return %_ValueOf(this);
31 // ECMA-262 section 15.5.4.3
32 function StringValueOf() {
33 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
34 throw new $TypeError('String.prototype.valueOf is not generic');
36 return %_ValueOf(this);
40 // ECMA-262, section 15.5.4.4
41 function StringCharAt(pos) {
42 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
44 var result = %_StringCharAt(this, pos);
45 if (%_IsSmi(result)) {
46 result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
52 // ECMA-262 section 15.5.4.5
53 function StringCharCodeAt(pos) {
54 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
56 var result = %_StringCharCodeAt(this, pos);
57 if (!%_IsSmi(result)) {
58 result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
64 // ECMA-262, section 15.5.4.6
65 function StringConcat(other /* and more */) { // length == 1
66 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
68 var len = %_ArgumentsLength();
69 var this_as_string = TO_STRING_INLINE(this);
71 return this_as_string + other;
73 var parts = new InternalArray(len + 1);
74 parts[0] = this_as_string;
75 for (var i = 0; i < len; i++) {
76 var part = %_Arguments(i);
77 parts[i + 1] = TO_STRING_INLINE(part);
79 return %StringBuilderConcat(parts, len + 1, "");
83 // ECMA-262 section 15.5.4.7
84 function StringIndexOfJS(pattern /* position */) { // length == 1
85 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
87 var subject = TO_STRING_INLINE(this);
88 pattern = TO_STRING_INLINE(pattern);
90 if (%_ArgumentsLength() > 1) {
91 index = %_Arguments(1); // position
92 index = TO_INTEGER(index);
93 if (index < 0) index = 0;
94 if (index > subject.length) index = subject.length;
96 return %StringIndexOf(subject, pattern, index);
100 // ECMA-262 section 15.5.4.8
101 function StringLastIndexOfJS(pat /* position */) { // length == 1
102 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
104 var sub = TO_STRING_INLINE(this);
105 var subLength = sub.length;
106 var pat = TO_STRING_INLINE(pat);
107 var patLength = pat.length;
108 var index = subLength - patLength;
109 if (%_ArgumentsLength() > 1) {
110 var position = ToNumber(%_Arguments(1));
111 if (!NUMBER_IS_NAN(position)) {
112 position = TO_INTEGER(position);
116 if (position + patLength < subLength) {
124 return %StringLastIndexOf(sub, pat, index);
128 // ECMA-262 section 15.5.4.9
130 // This function is implementation specific. For now, we do not
131 // do anything locale specific.
132 function StringLocaleCompareJS(other) {
133 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
135 return %StringLocaleCompare(TO_STRING_INLINE(this),
136 TO_STRING_INLINE(other));
140 // ECMA-262 section 15.5.4.10
141 function StringMatchJS(regexp) {
142 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
144 var subject = TO_STRING_INLINE(this);
145 if (IS_REGEXP(regexp)) {
146 // Emulate RegExp.prototype.exec's side effect in step 5, even though
147 // value is discarded.
148 var lastIndex = regexp.lastIndex;
149 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
150 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
151 // lastMatchInfo is defined in regexp.js.
152 var result = %StringMatch(subject, regexp, lastMatchInfo);
153 if (result !== null) lastMatchInfoOverride = null;
154 regexp.lastIndex = 0;
157 // Non-regexp argument.
158 regexp = new $RegExp(regexp);
159 return RegExpExecNoTests(regexp, subject, 0);
163 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
166 // ECMA-262 v6, section 21.1.3.12
168 // For now we do nothing, as proper normalization requires big tables.
169 // If Intl is enabled, then i18n.js will override it and provide the the
170 // proper functionality.
171 function StringNormalizeJS(form) {
172 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
174 var form = form ? TO_STRING_INLINE(form) : 'NFC';
175 var normalizationForm = NORMALIZATION_FORMS.indexOf(form);
176 if (normalizationForm === -1) {
177 throw new $RangeError('The normalization form should be one of '
178 + NORMALIZATION_FORMS.join(', ') + '.');
181 return %_ValueOf(this);
185 // This has the same size as the lastMatchInfo array, and can be used for
186 // functions that expect that structure to be returned. It is used when the
187 // needle is a string rather than a regexp. In this case we can't update
188 // lastMatchArray without erroneously affecting the properties on the global
190 var reusableMatchInfo = [2, "", "", -1, -1];
193 // ECMA-262, section 15.5.4.11
194 function StringReplace(search, replace) {
195 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
197 var subject = TO_STRING_INLINE(this);
199 // Decision tree for dispatch
201 // .... string replace
202 // ...... non-global search
203 // ........ empty string replace
204 // ........ non-empty string replace (with $-expansion)
205 // ...... global search
206 // ........ no need to circumvent last match info override
207 // ........ need to circument last match info override
208 // .... function replace
209 // ...... global search
210 // ...... non-global search
212 // .... special case that replaces with one single character
213 // ...... function replace
214 // ...... string replace (with $-expansion)
216 if (IS_REGEXP(search)) {
217 // Emulate RegExp.prototype.exec's side effect in step 5, even if
218 // value is discarded.
219 var lastIndex = search.lastIndex;
220 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
222 if (!IS_SPEC_FUNCTION(replace)) {
223 replace = TO_STRING_INLINE(replace);
225 if (!search.global) {
226 // Non-global regexp search, string replace.
227 var match = DoRegExpExec(search, subject, 0);
232 if (replace.length == 0) {
233 return %_SubString(subject, 0, match[CAPTURE0]) +
234 %_SubString(subject, match[CAPTURE1], subject.length)
236 return ExpandReplacement(replace, subject, lastMatchInfo,
237 %_SubString(subject, 0, match[CAPTURE0])) +
238 %_SubString(subject, match[CAPTURE1], subject.length);
241 // Global regexp search, string replace.
242 search.lastIndex = 0;
243 if (lastMatchInfoOverride == null) {
244 return %StringReplaceGlobalRegExpWithString(
245 subject, search, replace, lastMatchInfo);
247 // We use this hack to detect whether StringReplaceRegExpWithString
248 // found at least one hit. In that case we need to remove any
250 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX];
251 lastMatchInfo[LAST_SUBJECT_INDEX] = 0;
252 var answer = %StringReplaceGlobalRegExpWithString(
253 subject, search, replace, lastMatchInfo);
254 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
255 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
257 lastMatchInfoOverride = null;
264 // Global regexp search, function replace.
265 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
267 // Non-global regexp search, function replace.
268 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
271 search = TO_STRING_INLINE(search);
273 if (search.length == 1 &&
274 subject.length > 0xFF &&
275 IS_STRING(replace) &&
276 %StringIndexOf(replace, '$', 0) < 0) {
277 // Searching by traversing a cons string tree and replace with cons of
278 // slices works only when the replaced string is a single character, being
279 // replaced by a simple string and only pays off for long strings.
280 return %StringReplaceOneCharWithString(subject, search, replace);
282 var start = %StringIndexOf(subject, search, 0);
283 if (start < 0) return subject;
284 var end = start + search.length;
286 var result = %_SubString(subject, 0, start);
288 // Compute the string to replace with.
289 if (IS_SPEC_FUNCTION(replace)) {
290 var receiver = %GetDefaultReceiver(replace);
291 result += %_CallFunction(receiver, search, start, subject, replace);
293 reusableMatchInfo[CAPTURE0] = start;
294 reusableMatchInfo[CAPTURE1] = end;
295 result = ExpandReplacement(TO_STRING_INLINE(replace),
301 return result + %_SubString(subject, end, subject.length);
305 // Expand the $-expressions in the string and return a new string with
307 function ExpandReplacement(string, subject, matchInfo, result) {
308 var length = string.length;
309 var next = %StringIndexOf(string, '$', 0);
311 if (length > 0) result += string;
315 if (next > 0) result += %_SubString(string, 0, next);
319 var position = next + 1;
320 if (position < length) {
321 var peek = %_StringCharCodeAt(string, position);
322 if (peek == 36) { // $$
325 } else if (peek == 38) { // $& - match
328 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
329 } else if (peek == 96) { // $` - prefix
331 result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
332 } else if (peek == 39) { // $' - suffix
334 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
335 } else if (peek >= 48 && peek <= 57) {
336 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
337 var scaled_index = (peek - 48) << 1;
339 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
340 if (position + 1 < string.length) {
341 var next = %_StringCharCodeAt(string, position + 1);
342 if (next >= 48 && next <= 57) {
343 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
344 if (new_scaled_index < number_of_captures) {
345 scaled_index = new_scaled_index;
350 if (scaled_index != 0 && scaled_index < number_of_captures) {
351 var start = matchInfo[CAPTURE(scaled_index)];
354 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
367 // Go the the next $ in the string.
368 next = %StringIndexOf(string, '$', position);
370 // Return if there are no more $ characters in the string. If we
371 // haven't reached the end, we need to append the suffix.
373 if (position < length) {
374 result += %_SubString(string, position, length);
379 // Append substring between the previous and the next $ character.
380 if (next > position) {
381 result += %_SubString(string, position, next);
388 // Compute the string of a given regular expression capture.
389 function CaptureString(string, lastCaptureInfo, index) {
391 var scaled = index << 1;
392 // Compute start and end.
393 var start = lastCaptureInfo[CAPTURE(scaled)];
394 // If start isn't valid, return undefined.
395 if (start < 0) return;
396 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
397 return %_SubString(string, start, end);
401 // TODO(lrn): This array will survive indefinitely if replace is never
402 // called again. However, it will be empty, since the contents are cleared
403 // in the finally block.
404 var reusableReplaceArray = new InternalArray(16);
406 // Helper function for replacing regular expressions with the result of a
407 // function application in String.prototype.replace.
408 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
409 var resultArray = reusableReplaceArray;
411 reusableReplaceArray = null;
413 // Inside a nested replace (replace called from the replacement function
414 // of another replace) or we have failed to set the reusable array
415 // back due to an exception in a replacement function. Create a new
416 // array to use in the future, or until the original is written back.
417 resultArray = new InternalArray(16);
419 var res = %RegExpExecMultiple(regexp,
423 regexp.lastIndex = 0;
425 // No matches at all.
426 reusableReplaceArray = resultArray;
429 var len = res.length;
430 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
431 // If the number of captures is two then there are no explicit captures in
432 // the regexp, just the implicit capture that captures the whole match. In
433 // this case we can simplify quite a bit and end up with something faster.
434 // The builder will consist of some integers that indicate slices of the
435 // input string and some replacements that were returned from the replace
438 var override = new InternalPackedArray(null, 0, subject);
439 var receiver = %GetDefaultReceiver(replace);
440 for (var i = 0; i < len; i++) {
443 // Integers represent slices of the original string. Use these to
444 // get the offsets we need for the override array (so things like
445 // RegExp.leftContext work during the callback function.
447 match_start = (elem >> 11) + (elem & 0x7ff);
449 match_start = res[++i] - elem;
453 override[1] = match_start;
454 lastMatchInfoOverride = override;
456 %_CallFunction(receiver, elem, match_start, subject, replace);
457 // Overwrite the i'th element in the results with the string we got
458 // back from the callback function.
459 res[i] = TO_STRING_INLINE(func_result);
460 match_start += elem.length;
464 var receiver = %GetDefaultReceiver(replace);
465 for (var i = 0; i < len; i++) {
467 if (!%_IsSmi(elem)) {
468 // elem must be an Array.
469 // Use the apply argument as backing for global RegExp properties.
470 lastMatchInfoOverride = elem;
471 var func_result = %Apply(replace, receiver, elem, 0, elem.length);
472 // Overwrite the i'th element in the results with the string we got
473 // back from the callback function.
474 res[i] = TO_STRING_INLINE(func_result);
478 var result = %StringBuilderConcat(res, res.length, subject);
479 resultArray.length = 0;
480 reusableReplaceArray = resultArray;
485 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
486 var matchInfo = DoRegExpExec(regexp, subject, 0);
487 if (IS_NULL(matchInfo)) {
488 regexp.lastIndex = 0;
491 var index = matchInfo[CAPTURE0];
492 var result = %_SubString(subject, 0, index);
493 var endOfMatch = matchInfo[CAPTURE1];
494 // Compute the parameter list consisting of the match, captures, index,
495 // and subject for the replace function invocation.
496 // The number of captures plus one for the match.
497 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
499 var receiver = %GetDefaultReceiver(replace);
501 // No captures, only the match, which is always valid.
502 var s = %_SubString(subject, index, endOfMatch);
503 // Don't call directly to avoid exposing the built-in global object.
504 replacement = %_CallFunction(receiver, s, index, subject, replace);
506 var parameters = new InternalArray(m + 2);
507 for (var j = 0; j < m; j++) {
508 parameters[j] = CaptureString(subject, matchInfo, j);
510 parameters[j] = index;
511 parameters[j + 1] = subject;
513 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
516 result += replacement; // The add method converts to string if necessary.
517 // Can't use matchInfo any more from here, since the function could
519 return result + %_SubString(subject, endOfMatch, subject.length);
523 // ECMA-262 section 15.5.4.12
524 function StringSearch(re) {
525 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
529 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
530 } else if (IS_REGEXP(re)) {
533 regexp = new $RegExp(re);
535 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
537 return match[CAPTURE0];
543 // ECMA-262 section 15.5.4.13
544 function StringSlice(start, end) {
545 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
547 var s = TO_STRING_INLINE(this);
548 var s_len = s.length;
549 var start_i = TO_INTEGER(start);
551 if (!IS_UNDEFINED(end)) {
552 end_i = TO_INTEGER(end);
561 if (start_i > s_len) {
577 if (end_i <= start_i) {
581 return %_SubString(s, start_i, end_i);
585 // ECMA-262 section 15.5.4.14
586 function StringSplitJS(separator, limit) {
587 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
589 var subject = TO_STRING_INLINE(this);
590 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
592 var length = subject.length;
593 if (!IS_REGEXP(separator)) {
594 var separator_string = TO_STRING_INLINE(separator);
596 if (limit === 0) return [];
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];
602 var separator_length = separator_string.length;
604 // If the separator string is empty then return the elements in the subject.
605 if (separator_length === 0) return %StringToArray(subject, limit);
607 var result = %StringSplit(subject, separator_string, limit);
612 if (limit === 0) return [];
614 // Separator is a regular expression.
615 return StringSplitOnRegExp(subject, separator, limit, length);
619 function StringSplitOnRegExp(subject, separator, limit, length) {
621 if (DoRegExpExec(separator, subject, 0, 0) != null) {
627 var currentIndex = 0;
630 var result = new InternalArray();
635 if (startIndex === length) {
636 result[result.length] = %_SubString(subject, currentIndex, length);
640 var matchInfo = DoRegExpExec(separator, subject, startIndex);
641 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
642 result[result.length] = %_SubString(subject, currentIndex, length);
645 var endIndex = matchInfo[CAPTURE1];
647 // We ignore a zero-length match at the currentIndex.
648 if (startIndex === endIndex && endIndex === currentIndex) {
653 result[result.length] = %_SubString(subject, currentIndex, startMatch);
655 if (result.length === limit) break;
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++];
662 result[result.length] = %_SubString(subject, start, end);
664 result[result.length] = UNDEFINED;
666 if (result.length === limit) break outer_loop;
669 startIndex = currentIndex = endIndex;
671 var array_result = [];
672 %MoveArrayContents(result, array_result);
677 // ECMA-262 section 15.5.4.15
678 function StringSubstring(start, end) {
679 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
681 var s = TO_STRING_INLINE(this);
682 var s_len = s.length;
684 var start_i = TO_INTEGER(start);
687 } else if (start_i > s_len) {
692 if (!IS_UNDEFINED(end)) {
693 end_i = TO_INTEGER(end);
697 if (end_i < 0) end_i = 0;
698 if (start_i > end_i) {
706 return %_SubString(s, start_i, end_i);
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");
714 var s = TO_STRING_INLINE(this);
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)) {
723 if (len <= 0) return '';
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)) {
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,
739 if (start < 0) start = 0;
743 var end = start + len;
744 if (end > s.length) end = s.length;
746 return %_SubString(s, start, end);
750 // ECMA-262, 15.5.4.16
751 function StringToLowerCaseJS() {
752 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
754 return %StringToLowerCase(TO_STRING_INLINE(this));
758 // ECMA-262, 15.5.4.17
759 function StringToLocaleLowerCase() {
760 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
762 return %StringToLowerCase(TO_STRING_INLINE(this));
766 // ECMA-262, 15.5.4.18
767 function StringToUpperCaseJS() {
768 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
770 return %StringToUpperCase(TO_STRING_INLINE(this));
774 // ECMA-262, 15.5.4.19
775 function StringToLocaleUpperCase() {
776 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
778 return %StringToUpperCase(TO_STRING_INLINE(this));
782 function StringTrimJS() {
783 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
785 return %StringTrim(TO_STRING_INLINE(this), true, true);
788 function StringTrimLeft() {
789 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
791 return %StringTrim(TO_STRING_INLINE(this), true, false);
794 function StringTrimRight() {
795 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
797 return %StringTrim(TO_STRING_INLINE(this), false, true);
801 // ECMA-262, section 15.5.3.2
802 function StringFromCharCode(code) {
803 var n = %_ArgumentsLength();
805 if (!%_IsSmi(code)) code = ToNumber(code);
806 return %_StringCharFromCode(code & 0xffff);
809 var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
811 for (i = 0; i < n; i++) {
812 var code = %_Arguments(i);
813 if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
814 if (code < 0) code = code & 0xffff;
815 if (code > 0xff) break;
816 %_OneByteSeqStringSetChar(i, code, one_byte);
818 if (i == n) return one_byte;
819 one_byte = %TruncateString(one_byte, i);
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 = ToNumber(code) & 0xffff;
825 %_TwoByteSeqStringSetChar(j, code, two_byte);
827 return one_byte + two_byte;
831 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
832 function HtmlEscape(str) {
833 return TO_STRING_INLINE(str).replace(/"/g, """);
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) + "\">" + this + "</a>";
844 // ES6 draft, revision 26 (2014-07-18), section B.2.3.3
845 function StringBig() {
846 CHECK_OBJECT_COERCIBLE(this, "String.prototype.big");
847 return "<big>" + this + "</big>";
851 // ES6 draft, revision 26 (2014-07-18), section B.2.3.4
852 function StringBlink() {
853 CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink");
854 return "<blink>" + this + "</blink>";
858 // ES6 draft, revision 26 (2014-07-18), section B.2.3.5
859 function StringBold() {
860 CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold");
861 return "<b>" + this + "</b>";
865 // ES6 draft, revision 26 (2014-07-18), section B.2.3.6
866 function StringFixed() {
867 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed");
868 return "<tt>" + this + "</tt>";
872 // ES6 draft, revision 26 (2014-07-18), section B.2.3.7
873 function StringFontcolor(color) {
874 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor");
875 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
879 // ES6 draft, revision 26 (2014-07-18), section B.2.3.8
880 function StringFontsize(size) {
881 CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize");
882 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
886 // ES6 draft, revision 26 (2014-07-18), section B.2.3.9
887 function StringItalics() {
888 CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics");
889 return "<i>" + this + "</i>";
893 // ES6 draft, revision 26 (2014-07-18), section B.2.3.10
894 function StringLink(s) {
895 CHECK_OBJECT_COERCIBLE(this, "String.prototype.link");
896 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
900 // ES6 draft, revision 26 (2014-07-18), section B.2.3.11
901 function StringSmall() {
902 CHECK_OBJECT_COERCIBLE(this, "String.prototype.small");
903 return "<small>" + this + "</small>";
907 // ES6 draft, revision 26 (2014-07-18), section B.2.3.12
908 function StringStrike() {
909 CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike");
910 return "<strike>" + this + "</strike>";
914 // ES6 draft, revision 26 (2014-07-18), section B.2.3.13
915 function StringSub() {
916 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub");
917 return "<sub>" + this + "</sub>";
921 // ES6 draft, revision 26 (2014-07-18), section B.2.3.14
922 function StringSup() {
923 CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup");
924 return "<sup>" + this + "</sup>";
927 // -------------------------------------------------------------------
929 function SetUpString() {
930 %CheckIsBootstrapping();
932 // Set the String function and constructor.
933 %SetCode($String, StringConstructor);
934 %FunctionSetPrototype($String, new $String());
936 // Set up the constructor property on the String prototype object.
937 %AddNamedProperty($String.prototype, "constructor", $String, DONT_ENUM);
939 // Set up the non-enumerable functions on the String object.
940 InstallFunctions($String, DONT_ENUM, $Array(
941 "fromCharCode", StringFromCharCode
944 // Set up the non-enumerable functions on the String prototype object.
945 InstallFunctions($String.prototype, DONT_ENUM, $Array(
946 "valueOf", StringValueOf,
947 "toString", StringToString,
948 "charAt", StringCharAt,
949 "charCodeAt", StringCharCodeAt,
950 "concat", StringConcat,
951 "indexOf", StringIndexOfJS,
952 "lastIndexOf", StringLastIndexOfJS,
953 "localeCompare", StringLocaleCompareJS,
954 "match", StringMatchJS,
955 "normalize", StringNormalizeJS,
956 "replace", StringReplace,
957 "search", StringSearch,
958 "slice", StringSlice,
959 "split", StringSplitJS,
960 "substring", StringSubstring,
961 "substr", StringSubstr,
962 "toLowerCase", StringToLowerCaseJS,
963 "toLocaleLowerCase", StringToLocaleLowerCase,
964 "toUpperCase", StringToUpperCaseJS,
965 "toLocaleUpperCase", StringToLocaleUpperCase,
966 "trim", StringTrimJS,
967 "trimLeft", StringTrimLeft,
968 "trimRight", StringTrimRight,
970 "anchor", StringAnchor,
971 "fontcolor", StringFontcolor,
972 "fontsize", StringFontsize,
974 "blink", StringBlink,
976 "fixed", StringFixed,
977 "italics", StringItalics,
978 "small", StringSmall,
979 "strike", StringStrike,