blob: a464f7ffa376f1cc4775fe3ece89ba5363ec20b5 [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 {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000240 return %StringReplaceRegExpWithString(subject,
241 search,
242 TO_STRING_INLINE(replace),
243 lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000244 }
245 }
246
247 // Convert the search argument to a string and search for it.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000248 search = TO_STRING_INLINE(search);
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000249 if (search.length == 1 &&
250 subject.length > 0xFF &&
251 IS_STRING(replace) &&
252 %StringIndexOf(replace, '$', 0) < 0) {
253 // Searching by traversing a cons string tree and replace with cons of
254 // slices works only when the replaced string is a single character, being
255 // replaced by a simple string and only pays off for long strings.
256 return %StringReplaceOneCharWithString(subject, search, replace);
257 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000258 var start = %StringIndexOf(subject, search, 0);
259 if (start < 0) return subject;
260 var end = start + search.length;
261
ager@chromium.org8bb60582008-12-11 12:02:20 +0000262 var builder = new ReplaceResultBuilder(subject);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000263 // prefix
ager@chromium.org8bb60582008-12-11 12:02:20 +0000264 builder.addSpecialSlice(0, start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000265
266 // Compute the string to replace with.
lrn@chromium.org34e60782011-09-15 07:25:40 +0000267 if (IS_SPEC_FUNCTION(replace)) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000268 var receiver = %GetDefaultReceiver(replace);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000269 builder.add(%_CallFunction(receiver,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000270 search,
271 start,
272 subject,
273 replace));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000275 reusableMatchInfo[CAPTURE0] = start;
276 reusableMatchInfo[CAPTURE1] = end;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000277 replace = TO_STRING_INLINE(replace);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000278 ExpandReplacement(replace, subject, reusableMatchInfo, builder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 }
280
281 // suffix
ager@chromium.org8bb60582008-12-11 12:02:20 +0000282 builder.addSpecialSlice(end, subject.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283
284 return builder.generate();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000285}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000286
287
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288// Expand the $-expressions in the string and return a new string with
289// the result.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000290function ExpandReplacement(string, subject, matchInfo, builder) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000291 var length = string.length;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000292 var builder_elements = builder.elements;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293 var next = %StringIndexOf(string, '$', 0);
294 if (next < 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000295 if (length > 0) builder_elements.push(string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000296 return;
297 }
298
299 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000300 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301
whesse@chromium.org023421e2010-12-21 12:19:12 +0000302 if (next > 0) builder_elements.push(SubString(string, 0, next));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303
304 while (true) {
305 var expansion = '$';
306 var position = next + 1;
307 if (position < length) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000308 var peek = %_StringCharCodeAt(string, position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309 if (peek == 36) { // $$
310 ++position;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000311 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 } else if (peek == 38) { // $& - match
313 ++position;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000314 builder.addSpecialSlice(matchInfo[CAPTURE0],
315 matchInfo[CAPTURE1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000316 } else if (peek == 96) { // $` - prefix
317 ++position;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000318 builder.addSpecialSlice(0, matchInfo[CAPTURE0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000319 } else if (peek == 39) { // $' - suffix
320 ++position;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000321 builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000322 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
323 ++position;
324 var n = peek - 48;
325 if (position < length) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000326 peek = %_StringCharCodeAt(string, position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000327 // $nn, 01 <= nn <= 99
328 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
329 var nn = n * 10 + (peek - 48);
330 if (nn < m) {
331 // If the two digit capture reference is within range of
332 // the captures, we use it instead of the single digit
333 // one. Otherwise, we fall back to using the single
334 // digit reference. This matches the behavior of
335 // SpiderMonkey.
336 ++position;
337 n = nn;
338 }
339 }
340 }
341 if (0 < n && n < m) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000342 addCaptureString(builder, matchInfo, n);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343 } else {
344 // Because of the captures range check in the parsing of two
345 // digit capture references, we can only enter here when a
346 // single digit capture reference is outside the range of
347 // captures.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000348 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000349 --position;
350 }
ager@chromium.org8c51fc92009-04-22 11:54:55 +0000351 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000352 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000353 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000354 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000355 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000356 }
357
ager@chromium.org8bb60582008-12-11 12:02:20 +0000358 // Go the the next $ in the string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000359 next = %StringIndexOf(string, '$', position);
360
361 // Return if there are no more $ characters in the string. If we
362 // haven't reached the end, we need to append the suffix.
363 if (next < 0) {
364 if (position < length) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000365 builder_elements.push(SubString(string, position, length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000366 }
367 return;
368 }
369
370 // Append substring between the previous and the next $ character.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000371 if (next > position) {
372 builder_elements.push(SubString(string, position, next));
373 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000374 }
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000375}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376
377
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000378// Compute the string of a given regular expression capture.
379function CaptureString(string, lastCaptureInfo, index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380 // Scale the index.
381 var scaled = index << 1;
382 // Compute start and end.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000383 var start = lastCaptureInfo[CAPTURE(scaled)];
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000384 // If start isn't valid, return undefined.
385 if (start < 0) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000386 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387 return SubString(string, start, end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000388}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000389
390
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000391// Add the string of a given regular expression capture to the
392// ReplaceResultBuilder
393function addCaptureString(builder, matchInfo, index) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000394 // Scale the index.
395 var scaled = index << 1;
396 // Compute start and end.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000397 var start = matchInfo[CAPTURE(scaled)];
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000398 if (start < 0) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000399 var end = matchInfo[CAPTURE(scaled + 1)];
ager@chromium.org8bb60582008-12-11 12:02:20 +0000400 builder.addSpecialSlice(start, end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000401}
ager@chromium.org8bb60582008-12-11 12:02:20 +0000402
lrn@chromium.org25156de2010-04-06 13:10:27 +0000403// TODO(lrn): This array will survive indefinitely if replace is never
404// called again. However, it will be empty, since the contents are cleared
405// in the finally block.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000406var reusableReplaceArray = new InternalArray(16);
ager@chromium.org8bb60582008-12-11 12:02:20 +0000407
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408// Helper function for replacing regular expressions with the result of a
lrn@chromium.org25156de2010-04-06 13:10:27 +0000409// function application in String.prototype.replace.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000410function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
411 var resultArray = reusableReplaceArray;
412 if (resultArray) {
413 reusableReplaceArray = null;
414 } else {
415 // Inside a nested replace (replace called from the replacement function
416 // of another replace) or we have failed to set the reusable array
417 // back due to an exception in a replacement function. Create a new
418 // array to use in the future, or until the original is written back.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000419 resultArray = new InternalArray(16);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000420 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000421 var res = %RegExpExecMultiple(regexp,
422 subject,
423 lastMatchInfo,
424 resultArray);
425 regexp.lastIndex = 0;
426 if (IS_NULL(res)) {
427 // No matches at all.
428 reusableReplaceArray = resultArray;
429 return subject;
430 }
431 var len = res.length;
432 var i = 0;
433 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
434 var match_start = 0;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000435 var override = new InternalArray(null, 0, subject);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000436 var receiver = %GetDefaultReceiver(replace);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000437 while (i < len) {
438 var elem = res[i];
439 if (%_IsSmi(elem)) {
440 if (elem > 0) {
441 match_start = (elem >> 11) + (elem & 0x7ff);
442 } else {
443 match_start = res[++i] - elem;
444 }
445 } else {
446 override[0] = elem;
447 override[1] = match_start;
448 lastMatchInfoOverride = override;
449 var func_result =
450 %_CallFunction(receiver, elem, match_start, subject, replace);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000451 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000452 match_start += elem.length;
453 }
454 i++;
455 }
456 } else {
lrn@chromium.org34e60782011-09-15 07:25:40 +0000457 var receiver = %GetDefaultReceiver(replace);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000458 while (i < len) {
459 var elem = res[i];
460 if (!%_IsSmi(elem)) {
461 // elem must be an Array.
462 // Use the apply argument as backing for global RegExp properties.
463 lastMatchInfoOverride = elem;
lrn@chromium.org34e60782011-09-15 07:25:40 +0000464 var func_result = %Apply(replace, receiver, elem, 0, elem.length);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000465 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000466 }
467 i++;
468 }
469 }
470 var resultBuilder = new ReplaceResultBuilder(subject, res);
471 var result = resultBuilder.generate();
472 resultArray.length = 0;
473 reusableReplaceArray = resultArray;
474 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000475}
476
477
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000478function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
479 var matchInfo = DoRegExpExec(regexp, subject, 0);
480 if (IS_NULL(matchInfo)) return subject;
481 var result = new ReplaceResultBuilder(subject);
482 var index = matchInfo[CAPTURE0];
483 result.addSpecialSlice(0, index);
484 var endOfMatch = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000485 // Compute the parameter list consisting of the match, captures, index,
486 // and subject for the replace function invocation.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487 // The number of captures plus one for the match.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000488 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000489 var replacement;
lrn@chromium.org34e60782011-09-15 07:25:40 +0000490 var receiver = %GetDefaultReceiver(replace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000491 if (m == 1) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000492 // No captures, only the match, which is always valid.
493 var s = SubString(subject, index, endOfMatch);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000494 // Don't call directly to avoid exposing the built-in global object.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000495 replacement =
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000496 %_CallFunction(receiver, s, index, subject, replace);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000497 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000498 var parameters = new InternalArray(m + 2);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000499 for (var j = 0; j < m; j++) {
500 parameters[j] = CaptureString(subject, matchInfo, j);
501 }
502 parameters[j] = index;
503 parameters[j + 1] = subject;
504
lrn@chromium.org34e60782011-09-15 07:25:40 +0000505 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000506 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000507
508 result.add(replacement); // The add method converts to string if necessary.
509 // Can't use matchInfo any more from here, since the function could
510 // overwrite it.
511 result.addSpecialSlice(endOfMatch, subject.length);
512 return result.generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000513}
514
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516// ECMA-262 section 15.5.4.12
lrn@chromium.org25156de2010-04-06 13:10:27 +0000517function StringSearch(re) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000518 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
519 throw MakeTypeError("called_on_null_or_undefined",
520 ["String.prototype.search"]);
521 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000522 var regexp;
523 if (IS_STRING(re)) {
524 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
525 } else if (IS_REGEXP(re)) {
526 regexp = re;
527 } else {
528 regexp = new $RegExp(re);
529 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000530 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000531 if (match) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000532 return match[CAPTURE0];
533 }
534 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000535}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000536
537
538// ECMA-262 section 15.5.4.13
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000539function StringSlice(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000540 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
541 throw MakeTypeError("called_on_null_or_undefined",
542 ["String.prototype.slice"]);
543 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000544 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000545 var s_len = s.length;
546 var start_i = TO_INTEGER(start);
547 var end_i = s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000548 if (end !== void 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000549 end_i = TO_INTEGER(end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000550 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551
552 if (start_i < 0) {
553 start_i += s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000554 if (start_i < 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555 start_i = 0;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000556 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557 } else {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000558 if (start_i > s_len) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000559 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000560 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561 }
562
563 if (end_i < 0) {
564 end_i += s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000565 if (end_i < 0) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000566 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000567 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000568 } else {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000569 if (end_i > s_len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000570 end_i = s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000571 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000572 }
573
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000574 if (end_i <= start_i) {
575 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000576 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000577
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000578 return SubString(s, start_i, end_i);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000579}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000580
581
582// ECMA-262 section 15.5.4.14
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583function StringSplit(separator, limit) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000584 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
585 throw MakeTypeError("called_on_null_or_undefined",
586 ["String.prototype.split"]);
587 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000588 var subject = TO_STRING_INLINE(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000589 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000591 // ECMA-262 says that if separator is undefined, the result should
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000592 // be an array of size 1 containing the entire string.
593 if (IS_UNDEFINED(separator)) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000594 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000595 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000597 var length = subject.length;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000598 if (!IS_REGEXP(separator)) {
599 separator = TO_STRING_INLINE(separator);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000600
601 if (limit === 0) return [];
602
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000603 var separator_length = separator.length;
604
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000605 // If the separator string is empty then return the elements in the subject.
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000606 if (separator_length === 0) return %StringToArray(subject, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000607
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000608 var result = %StringSplit(subject, separator, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000609
610 return result;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000611 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000612
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000613 if (limit === 0) return [];
614
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000615 // Separator is a regular expression.
616 return StringSplitOnRegExp(subject, separator, limit, length);
617}
618
619
620function StringSplitOnRegExp(subject, separator, limit, length) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000621 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000623 if (length === 0) {
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000624 if (DoRegExpExec(separator, subject, 0, 0) != null) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000625 return [];
626 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000627 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000628 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000629
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000630 var currentIndex = 0;
631 var startIndex = 0;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000632 var startMatch = 0;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000633 var result = [];
634
ager@chromium.org357bf652010-04-12 11:30:10 +0000635 outer_loop:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000636 while (true) {
637
638 if (startIndex === length) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000639 result.push(SubString(subject, currentIndex, length));
ager@chromium.org357bf652010-04-12 11:30:10 +0000640 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000642
whesse@chromium.org023421e2010-12-21 12:19:12 +0000643 var matchInfo = DoRegExpExec(separator, subject, startIndex);
644 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
645 result.push(SubString(subject, currentIndex, length));
ager@chromium.org357bf652010-04-12 11:30:10 +0000646 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000647 }
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000648 var endIndex = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000649
650 // We ignore a zero-length match at the currentIndex.
651 if (startIndex === endIndex && endIndex === currentIndex) {
652 startIndex++;
653 continue;
654 }
655
whesse@chromium.org023421e2010-12-21 12:19:12 +0000656 if (currentIndex + 1 == startMatch) {
657 result.push(%_StringCharAt(subject, currentIndex));
658 } else {
659 result.push(%_SubString(subject, currentIndex, startMatch));
660 }
661
ager@chromium.org357bf652010-04-12 11:30:10 +0000662 if (result.length === limit) break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000663
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000664 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
665 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
666 var start = matchInfo[i++];
667 var end = matchInfo[i++];
668 if (end != -1) {
669 if (start + 1 == end) {
670 result.push(%_StringCharAt(subject, start));
671 } else {
672 result.push(%_SubString(subject, start, end));
673 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000674 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000675 result.push(void 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000676 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000677 if (result.length === limit) break outer_loop;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000678 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000679
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000680 startIndex = currentIndex = endIndex;
681 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000682 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000683}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000684
685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000686// ECMA-262 section 15.5.4.15
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000687function StringSubstring(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000688 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
689 throw MakeTypeError("called_on_null_or_undefined",
690 ["String.prototype.subString"]);
691 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000692 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000693 var s_len = s.length;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000694
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695 var start_i = TO_INTEGER(start);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000696 if (start_i < 0) {
697 start_i = 0;
698 } else if (start_i > s_len) {
699 start_i = s_len;
700 }
701
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000702 var end_i = s_len;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000703 if (!IS_UNDEFINED(end)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000704 end_i = TO_INTEGER(end);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000705 if (end_i > s_len) {
706 end_i = s_len;
707 } else {
708 if (end_i < 0) end_i = 0;
709 if (start_i > end_i) {
710 var tmp = end_i;
711 end_i = start_i;
712 start_i = tmp;
713 }
714 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000715 }
716
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000717 return ((start_i + 1 == end_i)
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000718 ? %_StringCharAt(s, start_i)
719 : %_SubString(s, start_i, end_i));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000720}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000721
722
723// This is not a part of ECMA-262.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000724function StringSubstr(start, n) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000725 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
726 throw MakeTypeError("called_on_null_or_undefined",
727 ["String.prototype.substr"]);
728 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000729 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000730 var len;
731
732 // Correct n: If not given, set to string length; if explicitly
733 // set to undefined, zero, or negative, returns empty string.
734 if (n === void 0) {
735 len = s.length;
736 } else {
737 len = TO_INTEGER(n);
738 if (len <= 0) return '';
739 }
740
741 // Correct start: If not given (or undefined), set to zero; otherwise
742 // convert to integer and handle negative case.
743 if (start === void 0) {
744 start = 0;
745 } else {
746 start = TO_INTEGER(start);
747 // If positive, and greater than or equal to the string length,
748 // return empty string.
749 if (start >= s.length) return '';
750 // If negative and absolute value is larger than the string length,
751 // use zero.
752 if (start < 0) {
753 start += s.length;
754 if (start < 0) start = 0;
755 }
756 }
757
758 var end = start + len;
759 if (end > s.length) end = s.length;
760
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000761 return ((start + 1 == end)
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000762 ? %_StringCharAt(s, start)
763 : %_SubString(s, start, end));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000764}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000765
766
767// ECMA-262, 15.5.4.16
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000768function StringToLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000769 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
770 throw MakeTypeError("called_on_null_or_undefined",
771 ["String.prototype.toLowerCase"]);
772 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000773 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000774}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775
776
777// ECMA-262, 15.5.4.17
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000778function StringToLocaleLowerCase() {
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.toLocaleLowerCase"]);
782 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000783 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000784}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000785
786
787// ECMA-262, 15.5.4.18
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000788function StringToUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000789 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
790 throw MakeTypeError("called_on_null_or_undefined",
791 ["String.prototype.toUpperCase"]);
792 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000793 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000794}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795
796
797// ECMA-262, 15.5.4.19
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000798function StringToLocaleUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000799 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
800 throw MakeTypeError("called_on_null_or_undefined",
801 ["String.prototype.toLocaleUpperCase"]);
802 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000803 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000804}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000805
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000806// ES5, 15.5.4.20
807function StringTrim() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000808 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
809 throw MakeTypeError("called_on_null_or_undefined",
810 ["String.prototype.trim"]);
811 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000812 return %StringTrim(TO_STRING_INLINE(this), true, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000813}
814
815function StringTrimLeft() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000816 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
817 throw MakeTypeError("called_on_null_or_undefined",
818 ["String.prototype.trimLeft"]);
819 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000820 return %StringTrim(TO_STRING_INLINE(this), true, false);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000821}
822
823function StringTrimRight() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000824 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
825 throw MakeTypeError("called_on_null_or_undefined",
826 ["String.prototype.trimRight"]);
827 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000828 return %StringTrim(TO_STRING_INLINE(this), false, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000829}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000831var static_charcode_array = new InternalArray(4);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000832
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833// ECMA-262, section 15.5.3.2
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000834function StringFromCharCode(code) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000835 var n = %_ArgumentsLength();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000836 if (n == 1) {
837 if (!%_IsSmi(code)) code = ToNumber(code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000838 return %_StringCharFromCode(code & 0xffff);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000839 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840
841 // NOTE: This is not super-efficient, but it is necessary because we
842 // want to avoid converting to numbers from within the virtual
843 // machine. Maybe we can find another way of doing this?
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000844 var codes = static_charcode_array;
845 for (var i = 0; i < n; i++) {
846 var code = %_Arguments(i);
847 if (!%_IsSmi(code)) code = ToNumber(code);
848 codes[i] = code;
849 }
850 codes.length = n;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000851 return %StringFromCharCodeArray(codes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000852}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000853
854
855// Helper function for very basic XSS protection.
856function HtmlEscape(str) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000857 return TO_STRING_INLINE(str).replace(/</g, "&lt;")
858 .replace(/>/g, "&gt;")
859 .replace(/"/g, "&quot;")
860 .replace(/'/g, "&#039;");
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000861}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000862
863
864// Compatibility support for KJS.
865// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000866function StringLink(s) {
867 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
868}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000869
870
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000871function StringAnchor(name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000872 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000873}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000874
875
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000876function StringFontcolor(color) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000877 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000878}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000879
880
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000881function StringFontsize(size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000883}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884
885
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000886function StringBig() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 return "<big>" + this + "</big>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000888}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000889
890
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000891function StringBlink() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000892 return "<blink>" + this + "</blink>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000893}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
895
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000896function StringBold() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 return "<b>" + this + "</b>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000898}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899
900
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000901function StringFixed() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902 return "<tt>" + this + "</tt>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000903}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000904
905
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000906function StringItalics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000907 return "<i>" + this + "</i>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000908}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909
910
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000911function StringSmall() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000912 return "<small>" + this + "</small>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000913}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914
915
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000916function StringStrike() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000917 return "<strike>" + this + "</strike>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000918}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000919
920
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000921function StringSub() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000922 return "<sub>" + this + "</sub>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000923}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000924
925
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000926function StringSup() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000927 return "<sup>" + this + "</sup>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000928}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000929
930
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000931// ReplaceResultBuilder support.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000932function ReplaceResultBuilder(str) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000933 if (%_ArgumentsLength() > 1) {
934 this.elements = %_Arguments(1);
935 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000936 this.elements = new InternalArray();
lrn@chromium.org25156de2010-04-06 13:10:27 +0000937 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000938 this.special_string = str;
939}
940
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000941SetUpLockedPrototype(ReplaceResultBuilder,
942 $Array("elements", "special_string"), $Array(
943 "add", function(str) {
944 str = TO_STRING_INLINE(str);
945 if (str.length > 0) this.elements.push(str);
946 },
947 "addSpecialSlice", function(start, end) {
948 var len = end - start;
949 if (start < 0 || len <= 0) return;
950 if (start < 0x80000 && len < 0x800) {
951 this.elements.push((start << 11) | len);
952 } else {
953 // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength,
954 // so -len is a smi.
955 var elements = this.elements;
956 elements.push(-len);
957 elements.push(start);
958 }
959 },
960 "generate", function() {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000961 var elements = this.elements;
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000962 return %StringBuilderConcat(elements, elements.length, this.special_string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000963 }
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000964));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000965
966
967// -------------------------------------------------------------------
968
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000969function SetUpString() {
970 %CheckIsBootstrapping();
971 // Set up the constructor property on the String prototype object.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000972 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
973
974
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000975 // Set up the non-enumerable functions on the String object.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000976 InstallFunctions($String, DONT_ENUM, $Array(
977 "fromCharCode", StringFromCharCode
978 ));
979
980
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000981 // Set up the non-enumerable functions on the String prototype object.
ricow@chromium.org27bf2882011-11-17 08:34:43 +0000982 InstallFunctions($String.prototype, DONT_ENUM, $Array(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000983 "valueOf", StringValueOf,
984 "toString", StringToString,
985 "charAt", StringCharAt,
986 "charCodeAt", StringCharCodeAt,
987 "concat", StringConcat,
988 "indexOf", StringIndexOf,
989 "lastIndexOf", StringLastIndexOf,
990 "localeCompare", StringLocaleCompare,
991 "match", StringMatch,
992 "replace", StringReplace,
993 "search", StringSearch,
994 "slice", StringSlice,
995 "split", StringSplit,
996 "substring", StringSubstring,
997 "substr", StringSubstr,
998 "toLowerCase", StringToLowerCase,
999 "toLocaleLowerCase", StringToLocaleLowerCase,
1000 "toUpperCase", StringToUpperCase,
1001 "toLocaleUpperCase", StringToLocaleUpperCase,
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001002 "trim", StringTrim,
1003 "trimLeft", StringTrimLeft,
1004 "trimRight", StringTrimRight,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001005 "link", StringLink,
1006 "anchor", StringAnchor,
1007 "fontcolor", StringFontcolor,
1008 "fontsize", StringFontsize,
1009 "big", StringBig,
1010 "blink", StringBlink,
1011 "bold", StringBold,
1012 "fixed", StringFixed,
1013 "italics", StringItalics,
1014 "small", StringSmall,
1015 "strike", StringStrike,
1016 "sub", StringSub,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001017 "sup", StringSup
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001018 ));
1019}
1020
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001021SetUpString();