blob: 6115930b6c8ff69568f425b8b996a821bc93cb87 [file] [log] [blame]
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00001// Copyright 2012 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
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.
15//
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.
27
28
29// This file relies on the fact that the following declaration has been made
30// in runtime.js:
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +000031// var $String = global.String;
32// var $NaN = 0/0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000033
34
35// Set the String function and constructor.
36%SetCode($String, function(x) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +000037 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000038 if (%_IsConstructCall()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039 %_SetValueOf(this, value);
40 } else {
41 return value;
42 }
43});
44
45%FunctionSetPrototype($String, new $String());
46
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000047// ECMA-262 section 15.5.4.2
48function StringToString() {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000049 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000050 throw new $TypeError('String.prototype.toString is not generic');
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000051 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000052 return %_ValueOf(this);
53}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000054
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000055
56// ECMA-262 section 15.5.4.3
57function StringValueOf() {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000058 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000059 throw new $TypeError('String.prototype.valueOf is not generic');
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000060 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000061 return %_ValueOf(this);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000062}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000063
64
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000065// ECMA-262, section 15.5.4.4
66function StringCharAt(pos) {
lrn@chromium.org1c092762011-05-09 09:42:16 +000067 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
68 throw MakeTypeError("called_on_null_or_undefined",
69 ["String.prototype.charAt"]);
70 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000071 var result = %_StringCharAt(this, pos);
72 if (%_IsSmi(result)) {
73 result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
ager@chromium.org381abbb2009-02-25 13:23:22 +000074 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000075 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000076}
77
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000078
79// ECMA-262 section 15.5.4.5
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000080function StringCharCodeAt(pos) {
lrn@chromium.org1c092762011-05-09 09:42:16 +000081 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
82 throw MakeTypeError("called_on_null_or_undefined",
83 ["String.prototype.charCodeAt"]);
84 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000085 var result = %_StringCharCodeAt(this, pos);
86 if (!%_IsSmi(result)) {
87 result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
kasper.lund7276f142008-07-30 08:49:36 +000088 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000089 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000090}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000091
92
93// ECMA-262, section 15.5.4.6
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000094function StringConcat() {
lrn@chromium.org1c092762011-05-09 09:42:16 +000095 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000096 throw MakeTypeError("called_on_null_or_undefined",
97 ["String.prototype.concat"]);
lrn@chromium.org1c092762011-05-09 09:42:16 +000098 }
ager@chromium.org5c838252010-02-19 08:53:10 +000099 var len = %_ArgumentsLength();
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000100 var this_as_string = TO_STRING_INLINE(this);
ager@chromium.org5c838252010-02-19 08:53:10 +0000101 if (len === 1) {
102 return this_as_string + %_Arguments(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000103 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000104 var parts = new InternalArray(len + 1);
ager@chromium.org5c838252010-02-19 08:53:10 +0000105 parts[0] = this_as_string;
106 for (var i = 0; i < len; i++) {
107 var part = %_Arguments(i);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000108 parts[i + 1] = TO_STRING_INLINE(part);
ager@chromium.org5c838252010-02-19 08:53:10 +0000109 }
110 return %StringBuilderConcat(parts, len + 1, "");
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000111}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000112
113// Match ES3 and Safari
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000114%FunctionSetLength(StringConcat, 1);
115
116
117// ECMA-262 section 15.5.4.7
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000118function StringIndexOf(pattern /* position */) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000119 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
120 throw MakeTypeError("called_on_null_or_undefined",
121 ["String.prototype.indexOf"]);
122 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000123 var subject = TO_STRING_INLINE(this);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000124 pattern = TO_STRING_INLINE(pattern);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000125 var index = 0;
126 if (%_ArgumentsLength() > 1) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000127 index = %_Arguments(1); // position
128 index = TO_INTEGER(index);
129 if (index < 0) index = 0;
130 if (index > subject.length) index = subject.length;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000131 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000132 return %StringIndexOf(subject, pattern, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000133}
134
135
136// ECMA-262 section 15.5.4.8
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000137function StringLastIndexOf(pat /* position */) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000138 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
139 throw MakeTypeError("called_on_null_or_undefined",
140 ["String.prototype.lastIndexOf"]);
141 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000142 var sub = TO_STRING_INLINE(this);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000143 var subLength = sub.length;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000144 var pat = TO_STRING_INLINE(pat);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000145 var patLength = pat.length;
146 var index = subLength - patLength;
147 if (%_ArgumentsLength() > 1) {
148 var position = ToNumber(%_Arguments(1));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000149 if (!NUMBER_IS_NAN(position)) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000150 position = TO_INTEGER(position);
151 if (position < 0) {
152 position = 0;
153 }
154 if (position + patLength < subLength) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +0000155 index = position;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000156 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000157 }
158 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000159 if (index < 0) {
160 return -1;
161 }
162 return %StringLastIndexOf(sub, pat, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000163}
164
165
166// ECMA-262 section 15.5.4.9
167//
168// This function is implementation specific. For now, we do not
169// do anything locale specific.
170function StringLocaleCompare(other) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000171 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
172 throw MakeTypeError("called_on_null_or_undefined",
173 ["String.prototype.localeCompare"]);
174 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000175 if (%_ArgumentsLength() === 0) return 0;
rossberg@chromium.org28a37082011-08-22 11:03:23 +0000176 return %StringLocaleCompare(TO_STRING_INLINE(this),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000177 TO_STRING_INLINE(other));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000178}
179
180
181// ECMA-262 section 15.5.4.10
182function StringMatch(regexp) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000183 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
184 throw MakeTypeError("called_on_null_or_undefined",
185 ["String.prototype.match"]);
186 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000187 var subject = TO_STRING_INLINE(this);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000188 if (IS_REGEXP(regexp)) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000189 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000190 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
191 // lastMatchInfo is defined in regexp.js.
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000192 var result = %StringMatch(subject, regexp, lastMatchInfo);
193 if (result !== null) lastMatchInfoOverride = null;
194 return result;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000195 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000196 // Non-regexp argument.
197 regexp = new $RegExp(regexp);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000198 return RegExpExecNoTests(regexp, subject, 0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000199}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200
201
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.
205function SubString(string, start, end) {
206 // Use the one character string cache.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000207 if (start + 1 == end) return %_StringCharAt(string, start);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000208 return %_SubString(string, start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000209}
210
211
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000212// 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
216// RegExp object.
217var reusableMatchInfo = [2, "", "", -1, -1];
218
219
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000220// ECMA-262, section 15.5.4.11
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000221function StringReplace(search, replace) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000222 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
223 throw MakeTypeError("called_on_null_or_undefined",
224 ["String.prototype.replace"]);
225 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000226 var subject = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000227
228 // Delegate to one of the regular expression variants if necessary.
229 if (IS_REGEXP(search)) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000230 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
lrn@chromium.org34e60782011-09-15 07:25:40 +0000231 if (IS_SPEC_FUNCTION(replace)) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000232 if (search.global) {
233 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
234 } else {
235 return StringReplaceNonGlobalRegExpWithFunction(subject,
236 search,
237 replace);
238 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000239 } else {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000240 if (lastMatchInfoOverride == null) {
241 return %StringReplaceRegExpWithString(subject,
242 search,
243 TO_STRING_INLINE(replace),
244 lastMatchInfo);
245 } else {
246 // We use this hack to detect whether StringReplaceRegExpWithString
247 // found at least one hit. In that case we need to remove any
248 // override.
249 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX];
250 lastMatchInfo[LAST_SUBJECT_INDEX] = 0;
251 var answer = %StringReplaceRegExpWithString(subject,
252 search,
253 TO_STRING_INLINE(replace),
254 lastMatchInfo);
255 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
256 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
257 } else {
258 lastMatchInfoOverride = null;
259 }
260 return answer;
261 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262 }
263 }
264
265 // Convert the search argument to a string and search for it.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000266 search = TO_STRING_INLINE(search);
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000267 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);
275 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000276 var start = %StringIndexOf(subject, search, 0);
277 if (start < 0) return subject;
278 var end = start + search.length;
279
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000280 var result = SubString(subject, 0, start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000281
282 // Compute the string to replace with.
lrn@chromium.org34e60782011-09-15 07:25:40 +0000283 if (IS_SPEC_FUNCTION(replace)) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000284 var receiver = %GetDefaultReceiver(replace);
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000285 result += %_CallFunction(receiver, search, start, subject, replace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000286 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000287 reusableMatchInfo[CAPTURE0] = start;
288 reusableMatchInfo[CAPTURE1] = end;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000289 replace = TO_STRING_INLINE(replace);
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000290 result = ExpandReplacement(replace, subject, reusableMatchInfo, result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 }
292
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000293 return result + SubString(subject, end, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000294}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295
296
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297// Expand the $-expressions in the string and return a new string with
298// the result.
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000299function ExpandReplacement(string, subject, matchInfo, result) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000300 var length = string.length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 var next = %StringIndexOf(string, '$', 0);
302 if (next < 0) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000303 if (length > 0) result += string;
304 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000305 }
306
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000307 if (next > 0) result += SubString(string, 0, next);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000308
309 while (true) {
310 var expansion = '$';
311 var position = next + 1;
312 if (position < length) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000313 var peek = %_StringCharCodeAt(string, position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 if (peek == 36) { // $$
315 ++position;
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000316 result += '$';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000317 } else if (peek == 38) { // $& - match
318 ++position;
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000319 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320 } else if (peek == 96) { // $` - prefix
321 ++position;
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000322 result += SubString(subject, 0, matchInfo[CAPTURE0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000323 } else if (peek == 39) { // $' - suffix
324 ++position;
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000325 result += SubString(subject, matchInfo[CAPTURE1], subject.length);
ager@chromium.org8c51fc92009-04-22 11:54:55 +0000326 } else {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000327 result += '$';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000328 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000329 } else {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000330 result += '$';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331 }
332
ager@chromium.org8bb60582008-12-11 12:02:20 +0000333 // Go the the next $ in the string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000334 next = %StringIndexOf(string, '$', position);
335
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.
338 if (next < 0) {
339 if (position < length) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000340 result += SubString(string, position, length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000341 }
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000342 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343 }
344
345 // Append substring between the previous and the next $ character.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000346 if (next > position) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000347 result += SubString(string, position, next);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000348 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000349 }
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000350 return result;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000351}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000352
353
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000354// Compute the string of a given regular expression capture.
355function CaptureString(string, lastCaptureInfo, index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000356 // Scale the index.
357 var scaled = index << 1;
358 // Compute start and end.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000359 var start = lastCaptureInfo[CAPTURE(scaled)];
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000360 // If start isn't valid, return undefined.
361 if (start < 0) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000362 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000363 return SubString(string, start, end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000364}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000365
366
lrn@chromium.org25156de2010-04-06 13:10:27 +0000367// 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.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000370var reusableReplaceArray = new InternalArray(16);
ager@chromium.org8bb60582008-12-11 12:02:20 +0000371
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372// Helper function for replacing regular expressions with the result of a
lrn@chromium.org25156de2010-04-06 13:10:27 +0000373// function application in String.prototype.replace.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000374function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
375 var resultArray = reusableReplaceArray;
376 if (resultArray) {
377 reusableReplaceArray = null;
378 } else {
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.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000383 resultArray = new InternalArray(16);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000385 var res = %RegExpExecMultiple(regexp,
386 subject,
387 lastMatchInfo,
388 resultArray);
389 regexp.lastIndex = 0;
390 if (IS_NULL(res)) {
391 // No matches at all.
392 reusableReplaceArray = resultArray;
393 return subject;
394 }
395 var len = res.length;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000396 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000397 // 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
402 // function.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000403 var match_start = 0;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000404 var override = new InternalArray(null, 0, subject);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000405 var receiver = %GetDefaultReceiver(replace);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000406 for (var i = 0; i < len; i++) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000407 var elem = res[i];
408 if (%_IsSmi(elem)) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000409 // 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.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000412 if (elem > 0) {
413 match_start = (elem >> 11) + (elem & 0x7ff);
414 } else {
415 match_start = res[++i] - elem;
416 }
417 } else {
418 override[0] = elem;
419 override[1] = match_start;
420 lastMatchInfoOverride = override;
421 var func_result =
422 %_CallFunction(receiver, elem, match_start, subject, replace);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000423 // Overwrite the i'th element in the results with the string we got
424 // back from the callback function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000425 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000426 match_start += elem.length;
427 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000428 }
429 } else {
lrn@chromium.org34e60782011-09-15 07:25:40 +0000430 var receiver = %GetDefaultReceiver(replace);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000431 for (var i = 0; i < len; i++) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000432 var elem = res[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;
lrn@chromium.org34e60782011-09-15 07:25:40 +0000437 var func_result = %Apply(replace, receiver, elem, 0, elem.length);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000438 // Overwrite the i'th element in the results with the string we got
439 // back from the callback function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000440 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000441 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000442 }
443 }
444 var resultBuilder = new ReplaceResultBuilder(subject, res);
445 var result = resultBuilder.generate();
446 resultArray.length = 0;
447 reusableReplaceArray = resultArray;
448 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000449}
450
451
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000452function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
453 var matchInfo = DoRegExpExec(regexp, subject, 0);
454 if (IS_NULL(matchInfo)) return subject;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000455 var index = matchInfo[CAPTURE0];
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000456 var result = SubString(subject, 0, index);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000457 var endOfMatch = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000458 // Compute the parameter list consisting of the match, captures, index,
459 // and subject for the replace function invocation.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000460 // The number of captures plus one for the match.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000461 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000462 var replacement;
lrn@chromium.org34e60782011-09-15 07:25:40 +0000463 var receiver = %GetDefaultReceiver(replace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000464 if (m == 1) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000465 // No captures, only the match, which is always valid.
466 var s = SubString(subject, index, endOfMatch);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000467 // Don't call directly to avoid exposing the built-in global object.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +0000468 replacement = %_CallFunction(receiver, s, index, subject, replace);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000469 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000470 var parameters = new InternalArray(m + 2);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000471 for (var j = 0; j < m; j++) {
472 parameters[j] = CaptureString(subject, matchInfo, j);
473 }
474 parameters[j] = index;
475 parameters[j + 1] = subject;
476
lrn@chromium.org34e60782011-09-15 07:25:40 +0000477 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000478 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000479
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000480 result += replacement; // The add method converts to string if necessary.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000481 // Can't use matchInfo any more from here, since the function could
482 // overwrite it.
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000483 return result + SubString(subject, endOfMatch, subject.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000484}
485
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000486
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487// ECMA-262 section 15.5.4.12
lrn@chromium.org25156de2010-04-06 13:10:27 +0000488function StringSearch(re) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000489 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
490 throw MakeTypeError("called_on_null_or_undefined",
491 ["String.prototype.search"]);
492 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000493 var regexp;
494 if (IS_STRING(re)) {
495 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
496 } else if (IS_REGEXP(re)) {
497 regexp = re;
498 } else {
499 regexp = new $RegExp(re);
500 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000501 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000502 if (match) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000503 return match[CAPTURE0];
504 }
505 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000506}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000507
508
509// ECMA-262 section 15.5.4.13
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000510function StringSlice(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000511 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
512 throw MakeTypeError("called_on_null_or_undefined",
513 ["String.prototype.slice"]);
514 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000515 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516 var s_len = s.length;
517 var start_i = TO_INTEGER(start);
518 var end_i = s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000519 if (end !== void 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000520 end_i = TO_INTEGER(end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000521 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522
523 if (start_i < 0) {
524 start_i += s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000525 if (start_i < 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000526 start_i = 0;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000527 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000528 } else {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000529 if (start_i > s_len) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000530 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000531 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000532 }
533
534 if (end_i < 0) {
535 end_i += s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000536 if (end_i < 0) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000537 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000538 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000539 } else {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000540 if (end_i > s_len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000541 end_i = s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000542 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000543 }
544
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000545 if (end_i <= start_i) {
546 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000547 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000549 return SubString(s, start_i, end_i);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000550}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551
552
553// ECMA-262 section 15.5.4.14
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000554function StringSplit(separator, limit) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000555 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
556 throw MakeTypeError("called_on_null_or_undefined",
557 ["String.prototype.split"]);
558 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000559 var subject = TO_STRING_INLINE(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000560 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000561
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562 // ECMA-262 says that if separator is undefined, the result should
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000563 // be an array of size 1 containing the entire string.
564 if (IS_UNDEFINED(separator)) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000565 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000567
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000568 var length = subject.length;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000569 if (!IS_REGEXP(separator)) {
570 separator = TO_STRING_INLINE(separator);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000571
572 if (limit === 0) return [];
573
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000574 var separator_length = separator.length;
575
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000576 // If the separator string is empty then return the elements in the subject.
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000577 if (separator_length === 0) return %StringToArray(subject, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000578
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000579 var result = %StringSplit(subject, separator, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000580
581 return result;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000582 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000584 if (limit === 0) return [];
585
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000586 // Separator is a regular expression.
587 return StringSplitOnRegExp(subject, separator, limit, length);
588}
589
590
591function StringSplitOnRegExp(subject, separator, limit, length) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000592 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
593
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000594 if (length === 0) {
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000595 if (DoRegExpExec(separator, subject, 0, 0) != null) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000596 return [];
597 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000598 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000599 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000600
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000601 var currentIndex = 0;
602 var startIndex = 0;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000603 var startMatch = 0;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000604 var result = [];
605
ager@chromium.org357bf652010-04-12 11:30:10 +0000606 outer_loop:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000607 while (true) {
608
609 if (startIndex === length) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000610 result.push(SubString(subject, currentIndex, length));
ager@chromium.org357bf652010-04-12 11:30:10 +0000611 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000613
whesse@chromium.org023421e2010-12-21 12:19:12 +0000614 var matchInfo = DoRegExpExec(separator, subject, startIndex);
615 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
616 result.push(SubString(subject, currentIndex, length));
ager@chromium.org357bf652010-04-12 11:30:10 +0000617 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000618 }
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000619 var endIndex = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000620
621 // We ignore a zero-length match at the currentIndex.
622 if (startIndex === endIndex && endIndex === currentIndex) {
623 startIndex++;
624 continue;
625 }
626
whesse@chromium.org023421e2010-12-21 12:19:12 +0000627 if (currentIndex + 1 == startMatch) {
628 result.push(%_StringCharAt(subject, currentIndex));
629 } else {
630 result.push(%_SubString(subject, currentIndex, startMatch));
631 }
632
ager@chromium.org357bf652010-04-12 11:30:10 +0000633 if (result.length === limit) break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000634
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000635 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++];
639 if (end != -1) {
640 if (start + 1 == end) {
641 result.push(%_StringCharAt(subject, start));
642 } else {
643 result.push(%_SubString(subject, start, end));
644 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000645 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000646 result.push(void 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000647 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000648 if (result.length === limit) break outer_loop;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000649 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000650
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000651 startIndex = currentIndex = endIndex;
652 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000653 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000654}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000655
656
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000657// ECMA-262 section 15.5.4.15
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000658function StringSubstring(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000659 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
660 throw MakeTypeError("called_on_null_or_undefined",
661 ["String.prototype.subString"]);
662 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000663 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000664 var s_len = s.length;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000666 var start_i = TO_INTEGER(start);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 if (start_i < 0) {
668 start_i = 0;
669 } else if (start_i > s_len) {
670 start_i = s_len;
671 }
672
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000673 var end_i = s_len;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000674 if (!IS_UNDEFINED(end)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000675 end_i = TO_INTEGER(end);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000676 if (end_i > s_len) {
677 end_i = s_len;
678 } else {
679 if (end_i < 0) end_i = 0;
680 if (start_i > end_i) {
681 var tmp = end_i;
682 end_i = start_i;
683 start_i = tmp;
684 }
685 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000686 }
687
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000688 return ((start_i + 1 == end_i)
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000689 ? %_StringCharAt(s, start_i)
690 : %_SubString(s, start_i, end_i));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000691}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000692
693
694// This is not a part of ECMA-262.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000695function StringSubstr(start, n) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000696 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
697 throw MakeTypeError("called_on_null_or_undefined",
698 ["String.prototype.substr"]);
699 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000700 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000701 var len;
702
703 // Correct n: If not given, set to string length; if explicitly
704 // set to undefined, zero, or negative, returns empty string.
705 if (n === void 0) {
706 len = s.length;
707 } else {
708 len = TO_INTEGER(n);
709 if (len <= 0) return '';
710 }
711
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) {
715 start = 0;
716 } else {
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,
722 // use zero.
723 if (start < 0) {
724 start += s.length;
725 if (start < 0) start = 0;
726 }
727 }
728
729 var end = start + len;
730 if (end > s.length) end = s.length;
731
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000732 return ((start + 1 == end)
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000733 ? %_StringCharAt(s, start)
734 : %_SubString(s, start, end));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000735}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736
737
738// ECMA-262, 15.5.4.16
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000739function StringToLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000740 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
741 throw MakeTypeError("called_on_null_or_undefined",
742 ["String.prototype.toLowerCase"]);
743 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000744 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000745}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746
747
748// ECMA-262, 15.5.4.17
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000749function StringToLocaleLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000750 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
751 throw MakeTypeError("called_on_null_or_undefined",
752 ["String.prototype.toLocaleLowerCase"]);
753 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000754 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000755}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000756
757
758// ECMA-262, 15.5.4.18
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000759function StringToUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000760 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
761 throw MakeTypeError("called_on_null_or_undefined",
762 ["String.prototype.toUpperCase"]);
763 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000764 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000765}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766
767
768// ECMA-262, 15.5.4.19
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000769function StringToLocaleUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000770 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
771 throw MakeTypeError("called_on_null_or_undefined",
772 ["String.prototype.toLocaleUpperCase"]);
773 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000774 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000775}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000776
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000777// ES5, 15.5.4.20
778function StringTrim() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000779 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
780 throw MakeTypeError("called_on_null_or_undefined",
781 ["String.prototype.trim"]);
782 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000783 return %StringTrim(TO_STRING_INLINE(this), true, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000784}
785
786function StringTrimLeft() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000787 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
788 throw MakeTypeError("called_on_null_or_undefined",
789 ["String.prototype.trimLeft"]);
790 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000791 return %StringTrim(TO_STRING_INLINE(this), true, false);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000792}
793
794function StringTrimRight() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000795 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
796 throw MakeTypeError("called_on_null_or_undefined",
797 ["String.prototype.trimRight"]);
798 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000799 return %StringTrim(TO_STRING_INLINE(this), false, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000800}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000801
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000802var static_charcode_array = new InternalArray(4);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000803
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000804// ECMA-262, section 15.5.3.2
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000805function StringFromCharCode(code) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000806 var n = %_ArgumentsLength();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000807 if (n == 1) {
808 if (!%_IsSmi(code)) code = ToNumber(code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000809 return %_StringCharFromCode(code & 0xffff);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000810 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000811
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?
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000815 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);
819 codes[i] = code;
820 }
821 codes.length = n;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000822 return %StringFromCharCodeArray(codes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000823}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000824
825
826// Helper function for very basic XSS protection.
827function HtmlEscape(str) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000828 return TO_STRING_INLINE(str).replace(/</g, "&lt;")
829 .replace(/>/g, "&gt;")
830 .replace(/"/g, "&quot;")
831 .replace(/'/g, "&#039;");
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000832}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833
834
835// Compatibility support for KJS.
836// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000837function StringLink(s) {
838 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
839}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840
841
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000842function StringAnchor(name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000843 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000844}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000845
846
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000847function StringFontcolor(color) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000848 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000849}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850
851
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000852function StringFontsize(size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000853 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000854}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855
856
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000857function StringBig() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000858 return "<big>" + this + "</big>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000859}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860
861
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000862function StringBlink() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000863 return "<blink>" + this + "</blink>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000864}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000865
866
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000867function StringBold() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868 return "<b>" + this + "</b>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000869}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000870
871
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000872function StringFixed() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000873 return "<tt>" + this + "</tt>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000874}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000875
876
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000877function StringItalics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 return "<i>" + this + "</i>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000879}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000880
881
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000882function StringSmall() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000883 return "<small>" + this + "</small>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000884}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
886
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000887function StringStrike() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000888 return "<strike>" + this + "</strike>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000889}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890
891
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000892function StringSub() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893 return "<sub>" + this + "</sub>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000894}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000895
896
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000897function StringSup() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000898 return "<sup>" + this + "</sup>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000899}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000900
901
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000902// ReplaceResultBuilder support.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000903function ReplaceResultBuilder(str) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000904 if (%_ArgumentsLength() > 1) {
905 this.elements = %_Arguments(1);
906 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000907 this.elements = new InternalArray();
lrn@chromium.org25156de2010-04-06 13:10:27 +0000908 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 this.special_string = str;
910}
911
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000912SetUpLockedPrototype(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);
917 },
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);
923 } else {
924 // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength,
925 // so -len is a smi.
926 var elements = this.elements;
927 elements.push(-len);
928 elements.push(start);
929 }
930 },
931 "generate", function() {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000932 var elements = this.elements;
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000933 return %StringBuilderConcat(elements, elements.length, this.special_string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000934 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000935));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000936
937
938// -------------------------------------------------------------------
939
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000940function SetUpString() {
941 %CheckIsBootstrapping();
942 // Set up the constructor property on the String prototype object.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000943 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
944
945
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000946 // Set up the non-enumerable functions on the String object.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000947 InstallFunctions($String, DONT_ENUM, $Array(
948 "fromCharCode", StringFromCharCode
949 ));
950
951
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000952 // Set up the non-enumerable functions on the String prototype object.
ricow@chromium.org27bf2882011-11-17 08:34:43 +0000953 InstallFunctions($String.prototype, DONT_ENUM, $Array(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000954 "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,
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000973 "trim", StringTrim,
974 "trimLeft", StringTrimLeft,
975 "trimRight", StringTrimRight,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000976 "link", StringLink,
977 "anchor", StringAnchor,
978 "fontcolor", StringFontcolor,
979 "fontsize", StringFontsize,
980 "big", StringBig,
981 "blink", StringBlink,
982 "bold", StringBold,
983 "fixed", StringFixed,
984 "italics", StringItalics,
985 "small", StringSmall,
986 "strike", StringStrike,
987 "sub", StringSub,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000988 "sup", StringSup
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000989 ));
990}
991
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000992SetUpString();