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.
28 // This file relies on the fact that the following declaration has been made
30 // var $String = global.String;
32 // -------------------------------------------------------------------
34 function StringConstructor(x) {
35 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
36 if (%_IsConstructCall()) {
37 %_SetValueOf(this, value);
44 // ECMA-262 section 15.5.4.2
45 function StringToString() {
46 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
47 throw new $TypeError('String.prototype.toString is not generic');
49 return %_ValueOf(this);
53 // ECMA-262 section 15.5.4.3
54 function StringValueOf() {
55 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
56 throw new $TypeError('String.prototype.valueOf is not generic');
58 return %_ValueOf(this);
62 // ECMA-262, section 15.5.4.4
63 function StringCharAt(pos) {
64 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
66 var result = %_StringCharAt(this, pos);
67 if (%_IsSmi(result)) {
68 result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
74 // ECMA-262 section 15.5.4.5
75 function StringCharCodeAt(pos) {
76 CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
78 var result = %_StringCharCodeAt(this, pos);
79 if (!%_IsSmi(result)) {
80 result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
86 // ECMA-262, section 15.5.4.6
87 function StringConcat() {
88 CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
90 var len = %_ArgumentsLength();
91 var this_as_string = TO_STRING_INLINE(this);
93 return this_as_string + %_Arguments(0);
95 var parts = new InternalArray(len + 1);
96 parts[0] = this_as_string;
97 for (var i = 0; i < len; i++) {
98 var part = %_Arguments(i);
99 parts[i + 1] = TO_STRING_INLINE(part);
101 return %StringBuilderConcat(parts, len + 1, "");
104 // Match ES3 and Safari
105 %FunctionSetLength(StringConcat, 1);
108 // ECMA-262 section 15.5.4.7
109 function StringIndexOf(pattern /* position */) { // length == 1
110 CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
112 var subject = TO_STRING_INLINE(this);
113 pattern = TO_STRING_INLINE(pattern);
115 if (%_ArgumentsLength() > 1) {
116 index = %_Arguments(1); // position
117 index = TO_INTEGER(index);
118 if (index < 0) index = 0;
119 if (index > subject.length) index = subject.length;
121 return %StringIndexOf(subject, pattern, index);
125 // ECMA-262 section 15.5.4.8
126 function StringLastIndexOf(pat /* position */) { // length == 1
127 CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
129 var sub = TO_STRING_INLINE(this);
130 var subLength = sub.length;
131 var pat = TO_STRING_INLINE(pat);
132 var patLength = pat.length;
133 var index = subLength - patLength;
134 if (%_ArgumentsLength() > 1) {
135 var position = ToNumber(%_Arguments(1));
136 if (!NUMBER_IS_NAN(position)) {
137 position = TO_INTEGER(position);
141 if (position + patLength < subLength) {
149 return %StringLastIndexOf(sub, pat, index);
153 // ECMA-262 section 15.5.4.9
155 // This function is implementation specific. For now, we do not
156 // do anything locale specific.
157 function StringLocaleCompare(other) {
158 CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
160 return %StringLocaleCompare(TO_STRING_INLINE(this),
161 TO_STRING_INLINE(other));
165 // ECMA-262 section 15.5.4.10
166 function StringMatch(regexp) {
167 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
169 var subject = TO_STRING_INLINE(this);
170 if (IS_REGEXP(regexp)) {
171 // Emulate RegExp.prototype.exec's side effect in step 5, even though
172 // value is discarded.
173 var lastIndex = regexp.lastIndex;
174 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
175 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
176 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
177 // lastMatchInfo is defined in regexp.js.
178 var result = %StringMatch(subject, regexp, lastMatchInfo);
179 if (result !== null) lastMatchInfoOverride = null;
180 regexp.lastIndex = 0;
183 // Non-regexp argument.
184 regexp = new $RegExp(regexp);
185 return RegExpExecNoTests(regexp, subject, 0);
189 // This has the same size as the lastMatchInfo array, and can be used for
190 // functions that expect that structure to be returned. It is used when the
191 // needle is a string rather than a regexp. In this case we can't update
192 // lastMatchArray without erroneously affecting the properties on the global
194 var reusableMatchInfo = [2, "", "", -1, -1];
197 // ECMA-262, section 15.5.4.11
198 function StringReplace(search, replace) {
199 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
201 var subject = TO_STRING_INLINE(this);
203 // Decision tree for dispatch
205 // .... string replace
206 // ...... non-global search
207 // ........ empty string replace
208 // ........ non-empty string replace (with $-expansion)
209 // ...... global search
210 // ........ no need to circumvent last match info override
211 // ........ need to circument last match info override
212 // .... function replace
213 // ...... global search
214 // ...... non-global search
216 // .... special case that replaces with one single character
217 // ...... function replace
218 // ...... string replace (with $-expansion)
220 if (IS_REGEXP(search)) {
221 // Emulate RegExp.prototype.exec's side effect in step 5, even if
222 // value is discarded.
223 var lastIndex = search.lastIndex;
224 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
225 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
227 if (!IS_SPEC_FUNCTION(replace)) {
228 replace = TO_STRING_INLINE(replace);
230 if (!search.global) {
231 // Non-global regexp search, string replace.
232 var match = DoRegExpExec(search, subject, 0);
237 if (replace.length == 0) {
238 return %_SubString(subject, 0, match[CAPTURE0]) +
239 %_SubString(subject, match[CAPTURE1], subject.length)
241 return ExpandReplacement(replace, subject, lastMatchInfo,
242 %_SubString(subject, 0, match[CAPTURE0])) +
243 %_SubString(subject, match[CAPTURE1], subject.length);
246 // Global regexp search, string replace.
247 search.lastIndex = 0;
248 if (lastMatchInfoOverride == null) {
249 return %StringReplaceGlobalRegExpWithString(
250 subject, search, replace, lastMatchInfo);
252 // We use this hack to detect whether StringReplaceRegExpWithString
253 // found at least one hit. In that case we need to remove any
255 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX];
256 lastMatchInfo[LAST_SUBJECT_INDEX] = 0;
257 var answer = %StringReplaceGlobalRegExpWithString(
258 subject, search, replace, lastMatchInfo);
259 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
260 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
262 lastMatchInfoOverride = null;
269 // Global regexp search, function replace.
270 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
272 // Non-global regexp search, function replace.
273 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
276 search = TO_STRING_INLINE(search);
278 if (search.length == 1 &&
279 subject.length > 0xFF &&
280 IS_STRING(replace) &&
281 %StringIndexOf(replace, '$', 0) < 0) {
282 // Searching by traversing a cons string tree and replace with cons of
283 // slices works only when the replaced string is a single character, being
284 // replaced by a simple string and only pays off for long strings.
285 return %StringReplaceOneCharWithString(subject, search, replace);
287 var start = %StringIndexOf(subject, search, 0);
288 if (start < 0) return subject;
289 var end = start + search.length;
291 var result = %_SubString(subject, 0, start);
293 // Compute the string to replace with.
294 if (IS_SPEC_FUNCTION(replace)) {
295 var receiver = %GetDefaultReceiver(replace);
296 result += %_CallFunction(receiver, search, start, subject, replace);
298 reusableMatchInfo[CAPTURE0] = start;
299 reusableMatchInfo[CAPTURE1] = end;
300 result = ExpandReplacement(TO_STRING_INLINE(replace),
306 return result + %_SubString(subject, end, subject.length);
310 // Expand the $-expressions in the string and return a new string with
312 function ExpandReplacement(string, subject, matchInfo, result) {
313 var length = string.length;
314 var next = %StringIndexOf(string, '$', 0);
316 if (length > 0) result += string;
320 if (next > 0) result += %_SubString(string, 0, next);
324 var position = next + 1;
325 if (position < length) {
326 var peek = %_StringCharCodeAt(string, position);
327 if (peek == 36) { // $$
330 } else if (peek == 38) { // $& - match
333 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
334 } else if (peek == 96) { // $` - prefix
336 result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
337 } else if (peek == 39) { // $' - suffix
339 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
340 } else if (peek >= 48 && peek <= 57) {
341 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
342 var scaled_index = (peek - 48) << 1;
344 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
345 if (position + 1 < string.length) {
346 var next = %_StringCharCodeAt(string, position + 1);
347 if (next >= 48 && next <= 57) {
348 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
349 if (new_scaled_index < number_of_captures) {
350 scaled_index = new_scaled_index;
355 if (scaled_index != 0 && scaled_index < number_of_captures) {
356 var start = matchInfo[CAPTURE(scaled_index)];
359 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
372 // Go the the next $ in the string.
373 next = %StringIndexOf(string, '$', position);
375 // Return if there are no more $ characters in the string. If we
376 // haven't reached the end, we need to append the suffix.
378 if (position < length) {
379 result += %_SubString(string, position, length);
384 // Append substring between the previous and the next $ character.
385 if (next > position) {
386 result += %_SubString(string, position, next);
393 // Compute the string of a given regular expression capture.
394 function CaptureString(string, lastCaptureInfo, index) {
396 var scaled = index << 1;
397 // Compute start and end.
398 var start = lastCaptureInfo[CAPTURE(scaled)];
399 // If start isn't valid, return undefined.
400 if (start < 0) return;
401 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
402 return %_SubString(string, start, end);
406 // TODO(lrn): This array will survive indefinitely if replace is never
407 // called again. However, it will be empty, since the contents are cleared
408 // in the finally block.
409 var reusableReplaceArray = new InternalArray(16);
411 // Helper function for replacing regular expressions with the result of a
412 // function application in String.prototype.replace.
413 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
414 var resultArray = reusableReplaceArray;
416 reusableReplaceArray = null;
418 // Inside a nested replace (replace called from the replacement function
419 // of another replace) or we have failed to set the reusable array
420 // back due to an exception in a replacement function. Create a new
421 // array to use in the future, or until the original is written back.
422 resultArray = new InternalArray(16);
424 var res = %RegExpExecMultiple(regexp,
428 regexp.lastIndex = 0;
430 // No matches at all.
431 reusableReplaceArray = resultArray;
434 var len = res.length;
435 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
436 // If the number of captures is two then there are no explicit captures in
437 // the regexp, just the implicit capture that captures the whole match. In
438 // this case we can simplify quite a bit and end up with something faster.
439 // The builder will consist of some integers that indicate slices of the
440 // input string and some replacements that were returned from the replace
443 var override = new InternalPackedArray(null, 0, subject);
444 var receiver = %GetDefaultReceiver(replace);
445 for (var i = 0; i < len; i++) {
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.
452 match_start = (elem >> 11) + (elem & 0x7ff);
454 match_start = res[++i] - elem;
458 override[1] = match_start;
459 lastMatchInfoOverride = override;
461 %_CallFunction(receiver, elem, match_start, subject, replace);
462 // Overwrite the i'th element in the results with the string we got
463 // back from the callback function.
464 res[i] = TO_STRING_INLINE(func_result);
465 match_start += elem.length;
469 var receiver = %GetDefaultReceiver(replace);
470 for (var i = 0; i < len; i++) {
472 if (!%_IsSmi(elem)) {
473 // elem must be an Array.
474 // Use the apply argument as backing for global RegExp properties.
475 lastMatchInfoOverride = elem;
476 var func_result = %Apply(replace, receiver, elem, 0, elem.length);
477 // Overwrite the i'th element in the results with the string we got
478 // back from the callback function.
479 res[i] = TO_STRING_INLINE(func_result);
483 var result = %StringBuilderConcat(res, res.length, subject);
484 resultArray.length = 0;
485 reusableReplaceArray = resultArray;
490 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
491 var matchInfo = DoRegExpExec(regexp, subject, 0);
492 if (IS_NULL(matchInfo)) {
493 regexp.lastIndex = 0;
496 var index = matchInfo[CAPTURE0];
497 var result = %_SubString(subject, 0, index);
498 var endOfMatch = matchInfo[CAPTURE1];
499 // Compute the parameter list consisting of the match, captures, index,
500 // and subject for the replace function invocation.
501 // The number of captures plus one for the match.
502 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
504 var receiver = %GetDefaultReceiver(replace);
506 // No captures, only the match, which is always valid.
507 var s = %_SubString(subject, index, endOfMatch);
508 // Don't call directly to avoid exposing the built-in global object.
509 replacement = %_CallFunction(receiver, s, index, subject, replace);
511 var parameters = new InternalArray(m + 2);
512 for (var j = 0; j < m; j++) {
513 parameters[j] = CaptureString(subject, matchInfo, j);
515 parameters[j] = index;
516 parameters[j + 1] = subject;
518 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
521 result += replacement; // The add method converts to string if necessary.
522 // Can't use matchInfo any more from here, since the function could
524 return result + %_SubString(subject, endOfMatch, subject.length);
528 // ECMA-262 section 15.5.4.12
529 function StringSearch(re) {
530 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
534 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
535 } else if (IS_REGEXP(re)) {
538 regexp = new $RegExp(re);
540 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
542 return match[CAPTURE0];
548 // ECMA-262 section 15.5.4.13
549 function StringSlice(start, end) {
550 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
552 var s = TO_STRING_INLINE(this);
553 var s_len = s.length;
554 var start_i = TO_INTEGER(start);
556 if (!IS_UNDEFINED(end)) {
557 end_i = TO_INTEGER(end);
566 if (start_i > s_len) {
582 if (end_i <= start_i) {
586 return %_SubString(s, start_i, end_i);
590 // ECMA-262 section 15.5.4.14
591 function StringSplit(separator, limit) {
592 CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
594 var subject = TO_STRING_INLINE(this);
595 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
597 var length = subject.length;
598 if (!IS_REGEXP(separator)) {
599 var separator_string = TO_STRING_INLINE(separator);
601 if (limit === 0) return [];
603 // ECMA-262 says that if separator is undefined, the result should
604 // be an array of size 1 containing the entire string.
605 if (IS_UNDEFINED(separator)) return [subject];
607 var separator_length = separator_string.length;
609 // If the separator string is empty then return the elements in the subject.
610 if (separator_length === 0) return %StringToArray(subject, limit);
612 var result = %StringSplit(subject, separator_string, limit);
617 if (limit === 0) return [];
619 // Separator is a regular expression.
620 return StringSplitOnRegExp(subject, separator, limit, length);
624 var ArrayPushBuiltin = $Array.prototype.push;
626 function StringSplitOnRegExp(subject, separator, limit, length) {
627 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
630 if (DoRegExpExec(separator, subject, 0, 0) != null) {
636 var currentIndex = 0;
644 if (startIndex === length) {
645 %_CallFunction(result, %_SubString(subject, currentIndex, length),
650 var matchInfo = DoRegExpExec(separator, subject, startIndex);
651 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
652 %_CallFunction(result, %_SubString(subject, currentIndex, length),
656 var endIndex = matchInfo[CAPTURE1];
658 // We ignore a zero-length match at the currentIndex.
659 if (startIndex === endIndex && endIndex === currentIndex) {
664 %_CallFunction(result, %_SubString(subject, currentIndex, startMatch),
667 if (result.length === limit) break;
669 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
670 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
671 var start = matchInfo[i++];
672 var end = matchInfo[i++];
674 %_CallFunction(result, %_SubString(subject, start, end),
677 %_CallFunction(result, UNDEFINED, ArrayPushBuiltin);
679 if (result.length === limit) break outer_loop;
682 startIndex = currentIndex = endIndex;
688 // ECMA-262 section 15.5.4.15
689 function StringSubstring(start, end) {
690 CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
692 var s = TO_STRING_INLINE(this);
693 var s_len = s.length;
695 var start_i = TO_INTEGER(start);
698 } else if (start_i > s_len) {
703 if (!IS_UNDEFINED(end)) {
704 end_i = TO_INTEGER(end);
708 if (end_i < 0) end_i = 0;
709 if (start_i > end_i) {
717 return %_SubString(s, start_i, end_i);
721 // This is not a part of ECMA-262.
722 function StringSubstr(start, n) {
723 CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
725 var s = TO_STRING_INLINE(this);
728 // Correct n: If not given, set to string length; if explicitly
729 // set to undefined, zero, or negative, returns empty string.
730 if (IS_UNDEFINED(n)) {
734 if (len <= 0) return '';
737 // Correct start: If not given (or undefined), set to zero; otherwise
738 // convert to integer and handle negative case.
739 if (IS_UNDEFINED(start)) {
742 start = TO_INTEGER(start);
743 // If positive, and greater than or equal to the string length,
744 // return empty string.
745 if (start >= s.length) return '';
746 // If negative and absolute value is larger than the string length,
750 if (start < 0) start = 0;
754 var end = start + len;
755 if (end > s.length) end = s.length;
757 return %_SubString(s, start, end);
761 // ECMA-262, 15.5.4.16
762 function StringToLowerCase() {
763 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
765 return %StringToLowerCase(TO_STRING_INLINE(this));
769 // ECMA-262, 15.5.4.17
770 function StringToLocaleLowerCase() {
771 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
773 return %StringToLowerCase(TO_STRING_INLINE(this));
777 // ECMA-262, 15.5.4.18
778 function StringToUpperCase() {
779 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
781 return %StringToUpperCase(TO_STRING_INLINE(this));
785 // ECMA-262, 15.5.4.19
786 function StringToLocaleUpperCase() {
787 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
789 return %StringToUpperCase(TO_STRING_INLINE(this));
793 function StringTrim() {
794 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
796 return %StringTrim(TO_STRING_INLINE(this), true, true);
799 function StringTrimLeft() {
800 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
802 return %StringTrim(TO_STRING_INLINE(this), true, false);
805 function StringTrimRight() {
806 CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
808 return %StringTrim(TO_STRING_INLINE(this), false, true);
812 // ECMA-262, section 15.5.3.2
813 function StringFromCharCode(code) {
814 var n = %_ArgumentsLength();
816 if (!%_IsSmi(code)) code = ToNumber(code);
817 return %_StringCharFromCode(code & 0xffff);
820 var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
822 for (i = 0; i < n; i++) {
823 var code = %_Arguments(i);
824 if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
825 if (code < 0) code = code & 0xffff;
826 if (code > 0xff) break;
827 %_OneByteSeqStringSetChar(one_byte, i, code);
829 if (i == n) return one_byte;
830 one_byte = %TruncateString(one_byte, i);
832 var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING);
833 for (var j = 0; i < n; i++, j++) {
834 var code = %_Arguments(i);
835 if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
836 %_TwoByteSeqStringSetChar(two_byte, j, code);
838 return one_byte + two_byte;
842 // Helper function for very basic XSS protection.
843 function HtmlEscape(str) {
844 return TO_STRING_INLINE(str).replace(/</g, "<")
845 .replace(/>/g, ">")
846 .replace(/"/g, """)
847 .replace(/'/g, "'");
851 // Compatibility support for KJS.
852 // Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
853 function StringLink(s) {
854 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
858 function StringAnchor(name) {
859 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
863 function StringFontcolor(color) {
864 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
868 function StringFontsize(size) {
869 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
873 function StringBig() {
874 return "<big>" + this + "</big>";
878 function StringBlink() {
879 return "<blink>" + this + "</blink>";
883 function StringBold() {
884 return "<b>" + this + "</b>";
888 function StringFixed() {
889 return "<tt>" + this + "</tt>";
893 function StringItalics() {
894 return "<i>" + this + "</i>";
898 function StringSmall() {
899 return "<small>" + this + "</small>";
903 function StringStrike() {
904 return "<strike>" + this + "</strike>";
908 function StringSub() {
909 return "<sub>" + this + "</sub>";
913 function StringSup() {
914 return "<sup>" + this + "</sup>";
917 // -------------------------------------------------------------------
919 function SetUpString() {
920 %CheckIsBootstrapping();
922 // Set the String function and constructor.
923 %SetCode($String, StringConstructor);
924 %FunctionSetPrototype($String, new $String());
926 // Set up the constructor property on the String prototype object.
927 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
929 // Set up the non-enumerable functions on the String object.
930 InstallFunctions($String, DONT_ENUM, $Array(
931 "fromCharCode", StringFromCharCode
934 // Set up the non-enumerable functions on the String prototype object.
935 InstallFunctions($String.prototype, DONT_ENUM, $Array(
936 "valueOf", StringValueOf,
937 "toString", StringToString,
938 "charAt", StringCharAt,
939 "charCodeAt", StringCharCodeAt,
940 "concat", StringConcat,
941 "indexOf", StringIndexOf,
942 "lastIndexOf", StringLastIndexOf,
943 "localeCompare", StringLocaleCompare,
944 "match", StringMatch,
945 "replace", StringReplace,
946 "search", StringSearch,
947 "slice", StringSlice,
948 "split", StringSplit,
949 "substring", StringSubstring,
950 "substr", StringSubstr,
951 "toLowerCase", StringToLowerCase,
952 "toLocaleLowerCase", StringToLocaleLowerCase,
953 "toUpperCase", StringToUpperCase,
954 "toLocaleUpperCase", StringToLocaleUpperCase,
956 "trimLeft", StringTrimLeft,
957 "trimRight", StringTrimRight,
959 "anchor", StringAnchor,
960 "fontcolor", StringFontcolor,
961 "fontsize", StringFontsize,
963 "blink", StringBlink,
965 "fixed", StringFixed,
966 "italics", StringItalics,
967 "small", StringSmall,
968 "strike", StringStrike,