1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 // This file relies on the fact that the following declaration has been made
31 // var $String = global.String;
35 // Set the String function and constructor.
36 %SetCode($String, function(x) {
37 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
38 if (%_IsConstructCall()) {
39 %_SetValueOf(this, value);
45 %FunctionSetPrototype($String, new $String());
47 // ECMA-262 section 15.5.4.2
48 function StringToString() {
49 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
50 throw new $TypeError('String.prototype.toString is not generic');
52 return %_ValueOf(this);
56 // ECMA-262 section 15.5.4.3
57 function StringValueOf() {
58 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
59 throw new $TypeError('String.prototype.valueOf is not generic');
61 return %_ValueOf(this);
65 // ECMA-262, section 15.5.4.4
66 function StringCharAt(pos) {
67 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
68 throw MakeTypeError("called_on_null_or_undefined",
69 ["String.prototype.charAt"]);
71 var result = %_StringCharAt(this, pos);
72 if (%_IsSmi(result)) {
73 result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
79 // ECMA-262 section 15.5.4.5
80 function StringCharCodeAt(pos) {
81 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
82 throw MakeTypeError("called_on_null_or_undefined",
83 ["String.prototype.charCodeAt"]);
85 var result = %_StringCharCodeAt(this, pos);
86 if (!%_IsSmi(result)) {
87 result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
93 // ECMA-262, section 15.5.4.6
94 function StringConcat() {
95 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
96 throw MakeTypeError("called_on_null_or_undefined",
97 ["String.prototype.concat"]);
99 var len = %_ArgumentsLength();
100 var this_as_string = TO_STRING_INLINE(this);
102 return this_as_string + %_Arguments(0);
104 var parts = new InternalArray(len + 1);
105 parts[0] = this_as_string;
106 for (var i = 0; i < len; i++) {
107 var part = %_Arguments(i);
108 parts[i + 1] = TO_STRING_INLINE(part);
110 return %StringBuilderConcat(parts, len + 1, "");
113 // Match ES3 and Safari
114 %FunctionSetLength(StringConcat, 1);
117 // ECMA-262 section 15.5.4.7
118 function StringIndexOf(pattern /* position */) { // length == 1
119 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
120 throw MakeTypeError("called_on_null_or_undefined",
121 ["String.prototype.indexOf"]);
123 var subject = TO_STRING_INLINE(this);
124 pattern = TO_STRING_INLINE(pattern);
126 if (%_ArgumentsLength() > 1) {
127 index = %_Arguments(1); // position
128 index = TO_INTEGER(index);
129 if (index < 0) index = 0;
130 if (index > subject.length) index = subject.length;
132 return %StringIndexOf(subject, pattern, index);
136 // ECMA-262 section 15.5.4.8
137 function StringLastIndexOf(pat /* position */) { // length == 1
138 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
139 throw MakeTypeError("called_on_null_or_undefined",
140 ["String.prototype.lastIndexOf"]);
142 var sub = TO_STRING_INLINE(this);
143 var subLength = sub.length;
144 var pat = TO_STRING_INLINE(pat);
145 var patLength = pat.length;
146 var index = subLength - patLength;
147 if (%_ArgumentsLength() > 1) {
148 var position = ToNumber(%_Arguments(1));
149 if (!NUMBER_IS_NAN(position)) {
150 position = TO_INTEGER(position);
154 if (position + patLength < subLength) {
162 return %StringLastIndexOf(sub, pat, index);
166 // ECMA-262 section 15.5.4.9
168 // This function is implementation specific. For now, we do not
169 // do anything locale specific.
170 function StringLocaleCompare(other) {
171 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
172 throw MakeTypeError("called_on_null_or_undefined",
173 ["String.prototype.localeCompare"]);
175 if (%_ArgumentsLength() === 0) return 0;
176 return %StringLocaleCompare(TO_STRING_INLINE(this),
177 TO_STRING_INLINE(other));
181 // ECMA-262 section 15.5.4.10
182 function StringMatch(regexp) {
183 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
184 throw MakeTypeError("called_on_null_or_undefined",
185 ["String.prototype.match"]);
187 var subject = TO_STRING_INLINE(this);
188 if (IS_REGEXP(regexp)) {
189 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
190 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
191 // lastMatchInfo is defined in regexp.js.
192 var result = %StringMatch(subject, regexp, lastMatchInfo);
193 if (result !== null) lastMatchInfoOverride = null;
196 // Non-regexp argument.
197 regexp = new $RegExp(regexp);
198 return RegExpExecNoTests(regexp, subject, 0);
202 // SubString is an internal function that returns the sub string of 'string'.
203 // If resulting string is of length 1, we use the one character cache
204 // otherwise we call the runtime system.
205 function SubString(string, start, end) {
206 // Use the one character string cache.
207 if (start + 1 == end) return %_StringCharAt(string, start);
208 return %_SubString(string, start, end);
212 // This has the same size as the lastMatchInfo array, and can be used for
213 // functions that expect that structure to be returned. It is used when the
214 // needle is a string rather than a regexp. In this case we can't update
215 // lastMatchArray without erroneously affecting the properties on the global
217 var reusableMatchInfo = [2, "", "", -1, -1];
220 // ECMA-262, section 15.5.4.11
221 function StringReplace(search, replace) {
222 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
223 throw MakeTypeError("called_on_null_or_undefined",
224 ["String.prototype.replace"]);
226 var subject = TO_STRING_INLINE(this);
228 // Delegate to one of the regular expression variants if necessary.
229 if (IS_REGEXP(search)) {
230 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
231 if (IS_SPEC_FUNCTION(replace)) {
233 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
235 return StringReplaceNonGlobalRegExpWithFunction(subject,
240 if (lastMatchInfoOverride == null) {
241 return %StringReplaceRegExpWithString(subject,
243 TO_STRING_INLINE(replace),
246 // We use this hack to detect whether StringReplaceRegExpWithString
247 // found at least one hit. In that case we need to remove any
249 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX];
250 lastMatchInfo[LAST_SUBJECT_INDEX] = 0;
251 var answer = %StringReplaceRegExpWithString(subject,
253 TO_STRING_INLINE(replace),
255 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
256 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
258 lastMatchInfoOverride = null;
265 // Convert the search argument to a string and search for it.
266 search = TO_STRING_INLINE(search);
267 if (search.length == 1 &&
268 subject.length > 0xFF &&
269 IS_STRING(replace) &&
270 %StringIndexOf(replace, '$', 0) < 0) {
271 // Searching by traversing a cons string tree and replace with cons of
272 // slices works only when the replaced string is a single character, being
273 // replaced by a simple string and only pays off for long strings.
274 return %StringReplaceOneCharWithString(subject, search, replace);
276 var start = %StringIndexOf(subject, search, 0);
277 if (start < 0) return subject;
278 var end = start + search.length;
280 var result = SubString(subject, 0, start);
282 // Compute the string to replace with.
283 if (IS_SPEC_FUNCTION(replace)) {
284 var receiver = %GetDefaultReceiver(replace);
285 result += %_CallFunction(receiver, search, start, subject, replace);
287 reusableMatchInfo[CAPTURE0] = start;
288 reusableMatchInfo[CAPTURE1] = end;
289 replace = TO_STRING_INLINE(replace);
290 result = ExpandReplacement(replace, subject, reusableMatchInfo, result);
293 return result + SubString(subject, end, subject.length);
297 // Expand the $-expressions in the string and return a new string with
299 function ExpandReplacement(string, subject, matchInfo, result) {
300 var length = string.length;
301 var next = %StringIndexOf(string, '$', 0);
303 if (length > 0) result += string;
307 if (next > 0) result += SubString(string, 0, next);
311 var position = next + 1;
312 if (position < length) {
313 var peek = %_StringCharCodeAt(string, position);
314 if (peek == 36) { // $$
317 } else if (peek == 38) { // $& - match
319 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
320 } else if (peek == 96) { // $` - prefix
322 result += SubString(subject, 0, matchInfo[CAPTURE0]);
323 } else if (peek == 39) { // $' - suffix
325 result += SubString(subject, matchInfo[CAPTURE1], subject.length);
333 // Go the the next $ in the string.
334 next = %StringIndexOf(string, '$', position);
336 // Return if there are no more $ characters in the string. If we
337 // haven't reached the end, we need to append the suffix.
339 if (position < length) {
340 result += SubString(string, position, length);
345 // Append substring between the previous and the next $ character.
346 if (next > position) {
347 result += SubString(string, position, next);
354 // Compute the string of a given regular expression capture.
355 function CaptureString(string, lastCaptureInfo, index) {
357 var scaled = index << 1;
358 // Compute start and end.
359 var start = lastCaptureInfo[CAPTURE(scaled)];
360 // If start isn't valid, return undefined.
361 if (start < 0) return;
362 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
363 return SubString(string, start, end);
367 // TODO(lrn): This array will survive indefinitely if replace is never
368 // called again. However, it will be empty, since the contents are cleared
369 // in the finally block.
370 var reusableReplaceArray = new InternalArray(16);
372 // Helper function for replacing regular expressions with the result of a
373 // function application in String.prototype.replace.
374 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
375 var resultArray = reusableReplaceArray;
377 reusableReplaceArray = null;
379 // Inside a nested replace (replace called from the replacement function
380 // of another replace) or we have failed to set the reusable array
381 // back due to an exception in a replacement function. Create a new
382 // array to use in the future, or until the original is written back.
383 resultArray = new InternalArray(16);
385 var res = %RegExpExecMultiple(regexp,
389 regexp.lastIndex = 0;
391 // No matches at all.
392 reusableReplaceArray = resultArray;
395 var len = res.length;
396 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
397 // If the number of captures is two then there are no explicit captures in
398 // the regexp, just the implicit capture that captures the whole match. In
399 // this case we can simplify quite a bit and end up with something faster.
400 // The builder will consist of some integers that indicate slices of the
401 // input string and some replacements that were returned from the replace
404 var override = new InternalArray(null, 0, subject);
405 var receiver = %GetDefaultReceiver(replace);
406 for (var i = 0; i < len; i++) {
409 // Integers represent slices of the original string. Use these to
410 // get the offsets we need for the override array (so things like
411 // RegExp.leftContext work during the callback function.
413 match_start = (elem >> 11) + (elem & 0x7ff);
415 match_start = res[++i] - elem;
419 override[1] = match_start;
420 lastMatchInfoOverride = override;
422 %_CallFunction(receiver, elem, match_start, subject, replace);
423 // Overwrite the i'th element in the results with the string we got
424 // back from the callback function.
425 res[i] = TO_STRING_INLINE(func_result);
426 match_start += elem.length;
430 var receiver = %GetDefaultReceiver(replace);
431 for (var i = 0; i < len; i++) {
433 if (!%_IsSmi(elem)) {
434 // elem must be an Array.
435 // Use the apply argument as backing for global RegExp properties.
436 lastMatchInfoOverride = elem;
437 var func_result = %Apply(replace, receiver, elem, 0, elem.length);
438 // Overwrite the i'th element in the results with the string we got
439 // back from the callback function.
440 res[i] = TO_STRING_INLINE(func_result);
444 var resultBuilder = new ReplaceResultBuilder(subject, res);
445 var result = resultBuilder.generate();
446 resultArray.length = 0;
447 reusableReplaceArray = resultArray;
452 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
453 var matchInfo = DoRegExpExec(regexp, subject, 0);
454 if (IS_NULL(matchInfo)) return subject;
455 var index = matchInfo[CAPTURE0];
456 var result = SubString(subject, 0, index);
457 var endOfMatch = matchInfo[CAPTURE1];
458 // Compute the parameter list consisting of the match, captures, index,
459 // and subject for the replace function invocation.
460 // The number of captures plus one for the match.
461 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
463 var receiver = %GetDefaultReceiver(replace);
465 // No captures, only the match, which is always valid.
466 var s = SubString(subject, index, endOfMatch);
467 // Don't call directly to avoid exposing the built-in global object.
468 replacement = %_CallFunction(receiver, s, index, subject, replace);
470 var parameters = new InternalArray(m + 2);
471 for (var j = 0; j < m; j++) {
472 parameters[j] = CaptureString(subject, matchInfo, j);
474 parameters[j] = index;
475 parameters[j + 1] = subject;
477 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
480 result += replacement; // The add method converts to string if necessary.
481 // Can't use matchInfo any more from here, since the function could
483 return result + SubString(subject, endOfMatch, subject.length);
487 // ECMA-262 section 15.5.4.12
488 function StringSearch(re) {
489 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
490 throw MakeTypeError("called_on_null_or_undefined",
491 ["String.prototype.search"]);
495 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
496 } else if (IS_REGEXP(re)) {
499 regexp = new $RegExp(re);
501 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
503 return match[CAPTURE0];
509 // ECMA-262 section 15.5.4.13
510 function StringSlice(start, end) {
511 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
512 throw MakeTypeError("called_on_null_or_undefined",
513 ["String.prototype.slice"]);
515 var s = TO_STRING_INLINE(this);
516 var s_len = s.length;
517 var start_i = TO_INTEGER(start);
519 if (end !== void 0) {
520 end_i = TO_INTEGER(end);
529 if (start_i > s_len) {
545 if (end_i <= start_i) {
549 return SubString(s, start_i, end_i);
553 // ECMA-262 section 15.5.4.14
554 function StringSplit(separator, limit) {
555 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
556 throw MakeTypeError("called_on_null_or_undefined",
557 ["String.prototype.split"]);
559 var subject = TO_STRING_INLINE(this);
560 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
562 // ECMA-262 says that if separator is undefined, the result should
563 // be an array of size 1 containing the entire string.
564 if (IS_UNDEFINED(separator)) {
568 var length = subject.length;
569 if (!IS_REGEXP(separator)) {
570 separator = TO_STRING_INLINE(separator);
572 if (limit === 0) return [];
574 var separator_length = separator.length;
576 // If the separator string is empty then return the elements in the subject.
577 if (separator_length === 0) return %StringToArray(subject, limit);
579 var result = %StringSplit(subject, separator, limit);
584 if (limit === 0) return [];
586 // Separator is a regular expression.
587 return StringSplitOnRegExp(subject, separator, limit, length);
591 function StringSplitOnRegExp(subject, separator, limit, length) {
592 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
595 if (DoRegExpExec(separator, subject, 0, 0) != null) {
601 var currentIndex = 0;
609 if (startIndex === length) {
610 result.push(SubString(subject, currentIndex, length));
614 var matchInfo = DoRegExpExec(separator, subject, startIndex);
615 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
616 result.push(SubString(subject, currentIndex, length));
619 var endIndex = matchInfo[CAPTURE1];
621 // We ignore a zero-length match at the currentIndex.
622 if (startIndex === endIndex && endIndex === currentIndex) {
627 if (currentIndex + 1 == startMatch) {
628 result.push(%_StringCharAt(subject, currentIndex));
630 result.push(%_SubString(subject, currentIndex, startMatch));
633 if (result.length === limit) break;
635 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
636 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
637 var start = matchInfo[i++];
638 var end = matchInfo[i++];
640 if (start + 1 == end) {
641 result.push(%_StringCharAt(subject, start));
643 result.push(%_SubString(subject, start, end));
648 if (result.length === limit) break outer_loop;
651 startIndex = currentIndex = endIndex;
657 // ECMA-262 section 15.5.4.15
658 function StringSubstring(start, end) {
659 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
660 throw MakeTypeError("called_on_null_or_undefined",
661 ["String.prototype.subString"]);
663 var s = TO_STRING_INLINE(this);
664 var s_len = s.length;
666 var start_i = TO_INTEGER(start);
669 } else if (start_i > s_len) {
674 if (!IS_UNDEFINED(end)) {
675 end_i = TO_INTEGER(end);
679 if (end_i < 0) end_i = 0;
680 if (start_i > end_i) {
688 return ((start_i + 1 == end_i)
689 ? %_StringCharAt(s, start_i)
690 : %_SubString(s, start_i, end_i));
694 // This is not a part of ECMA-262.
695 function StringSubstr(start, n) {
696 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
697 throw MakeTypeError("called_on_null_or_undefined",
698 ["String.prototype.substr"]);
700 var s = TO_STRING_INLINE(this);
703 // Correct n: If not given, set to string length; if explicitly
704 // set to undefined, zero, or negative, returns empty string.
709 if (len <= 0) return '';
712 // Correct start: If not given (or undefined), set to zero; otherwise
713 // convert to integer and handle negative case.
714 if (start === void 0) {
717 start = TO_INTEGER(start);
718 // If positive, and greater than or equal to the string length,
719 // return empty string.
720 if (start >= s.length) return '';
721 // If negative and absolute value is larger than the string length,
725 if (start < 0) start = 0;
729 var end = start + len;
730 if (end > s.length) end = s.length;
732 return ((start + 1 == end)
733 ? %_StringCharAt(s, start)
734 : %_SubString(s, start, end));
738 // ECMA-262, 15.5.4.16
739 function StringToLowerCase() {
740 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
741 throw MakeTypeError("called_on_null_or_undefined",
742 ["String.prototype.toLowerCase"]);
744 return %StringToLowerCase(TO_STRING_INLINE(this));
748 // ECMA-262, 15.5.4.17
749 function StringToLocaleLowerCase() {
750 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
751 throw MakeTypeError("called_on_null_or_undefined",
752 ["String.prototype.toLocaleLowerCase"]);
754 return %StringToLowerCase(TO_STRING_INLINE(this));
758 // ECMA-262, 15.5.4.18
759 function StringToUpperCase() {
760 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
761 throw MakeTypeError("called_on_null_or_undefined",
762 ["String.prototype.toUpperCase"]);
764 return %StringToUpperCase(TO_STRING_INLINE(this));
768 // ECMA-262, 15.5.4.19
769 function StringToLocaleUpperCase() {
770 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
771 throw MakeTypeError("called_on_null_or_undefined",
772 ["String.prototype.toLocaleUpperCase"]);
774 return %StringToUpperCase(TO_STRING_INLINE(this));
778 function StringTrim() {
779 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
780 throw MakeTypeError("called_on_null_or_undefined",
781 ["String.prototype.trim"]);
783 return %StringTrim(TO_STRING_INLINE(this), true, true);
786 function StringTrimLeft() {
787 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
788 throw MakeTypeError("called_on_null_or_undefined",
789 ["String.prototype.trimLeft"]);
791 return %StringTrim(TO_STRING_INLINE(this), true, false);
794 function StringTrimRight() {
795 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
796 throw MakeTypeError("called_on_null_or_undefined",
797 ["String.prototype.trimRight"]);
799 return %StringTrim(TO_STRING_INLINE(this), false, true);
802 var static_charcode_array = new InternalArray(4);
804 // ECMA-262, section 15.5.3.2
805 function StringFromCharCode(code) {
806 var n = %_ArgumentsLength();
808 if (!%_IsSmi(code)) code = ToNumber(code);
809 return %_StringCharFromCode(code & 0xffff);
812 // NOTE: This is not super-efficient, but it is necessary because we
813 // want to avoid converting to numbers from within the virtual
814 // machine. Maybe we can find another way of doing this?
815 var codes = static_charcode_array;
816 for (var i = 0; i < n; i++) {
817 var code = %_Arguments(i);
818 if (!%_IsSmi(code)) code = ToNumber(code);
822 return %StringFromCharCodeArray(codes);
826 // Helper function for very basic XSS protection.
827 function HtmlEscape(str) {
828 return TO_STRING_INLINE(str).replace(/</g, "<")
829 .replace(/>/g, ">")
830 .replace(/"/g, """)
831 .replace(/'/g, "'");
835 // Compatibility support for KJS.
836 // Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
837 function StringLink(s) {
838 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
842 function StringAnchor(name) {
843 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
847 function StringFontcolor(color) {
848 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
852 function StringFontsize(size) {
853 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
857 function StringBig() {
858 return "<big>" + this + "</big>";
862 function StringBlink() {
863 return "<blink>" + this + "</blink>";
867 function StringBold() {
868 return "<b>" + this + "</b>";
872 function StringFixed() {
873 return "<tt>" + this + "</tt>";
877 function StringItalics() {
878 return "<i>" + this + "</i>";
882 function StringSmall() {
883 return "<small>" + this + "</small>";
887 function StringStrike() {
888 return "<strike>" + this + "</strike>";
892 function StringSub() {
893 return "<sub>" + this + "</sub>";
897 function StringSup() {
898 return "<sup>" + this + "</sup>";
902 // ReplaceResultBuilder support.
903 function ReplaceResultBuilder(str) {
904 if (%_ArgumentsLength() > 1) {
905 this.elements = %_Arguments(1);
907 this.elements = new InternalArray();
909 this.special_string = str;
912 SetUpLockedPrototype(ReplaceResultBuilder,
913 $Array("elements", "special_string"), $Array(
914 "add", function(str) {
915 str = TO_STRING_INLINE(str);
916 if (str.length > 0) this.elements.push(str);
918 "addSpecialSlice", function(start, end) {
919 var len = end - start;
920 if (start < 0 || len <= 0) return;
921 if (start < 0x80000 && len < 0x800) {
922 this.elements.push((start << 11) | len);
924 // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength,
926 var elements = this.elements;
928 elements.push(start);
931 "generate", function() {
932 var elements = this.elements;
933 return %StringBuilderConcat(elements, elements.length, this.special_string);
938 // -------------------------------------------------------------------
940 function SetUpString() {
941 %CheckIsBootstrapping();
942 // Set up the constructor property on the String prototype object.
943 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
946 // Set up the non-enumerable functions on the String object.
947 InstallFunctions($String, DONT_ENUM, $Array(
948 "fromCharCode", StringFromCharCode
952 // Set up the non-enumerable functions on the String prototype object.
953 InstallFunctions($String.prototype, DONT_ENUM, $Array(
954 "valueOf", StringValueOf,
955 "toString", StringToString,
956 "charAt", StringCharAt,
957 "charCodeAt", StringCharCodeAt,
958 "concat", StringConcat,
959 "indexOf", StringIndexOf,
960 "lastIndexOf", StringLastIndexOf,
961 "localeCompare", StringLocaleCompare,
962 "match", StringMatch,
963 "replace", StringReplace,
964 "search", StringSearch,
965 "slice", StringSlice,
966 "split", StringSplit,
967 "substring", StringSubstring,
968 "substr", StringSubstr,
969 "toLowerCase", StringToLowerCase,
970 "toLocaleLowerCase", StringToLocaleLowerCase,
971 "toUpperCase", StringToUpperCase,
972 "toLocaleUpperCase", StringToLocaleUpperCase,
974 "trimLeft", StringTrimLeft,
975 "trimRight", StringTrimRight,
977 "anchor", StringAnchor,
978 "fontcolor", StringFontcolor,
979 "fontsize", StringFontsize,
981 "blink", StringBlink,
983 "fixed", StringFixed,
984 "italics", StringItalics,
985 "small", StringSmall,
986 "strike", StringStrike,