blob: cb82c166346fc7ba5491a1e9e5d28e0e9b5a5108 [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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000028// This file relies on the fact that the following declaration has been made
29// in runtime.js:
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +000030// var $String = global.String;
31// var $NaN = 0/0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000033// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000034
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000035function StringConstructor(x) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +000036 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000037 if (%_IsConstructCall()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038 %_SetValueOf(this, value);
39 } else {
40 return value;
41 }
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000042}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000045// ECMA-262 section 15.5.4.2
46function StringToString() {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000047 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000048 throw new $TypeError('String.prototype.toString is not generic');
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000049 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000050 return %_ValueOf(this);
51}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000052
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000053
54// ECMA-262 section 15.5.4.3
55function StringValueOf() {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000056 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057 throw new $TypeError('String.prototype.valueOf is not generic');
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000058 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000059 return %_ValueOf(this);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000060}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000061
62
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000063// ECMA-262, section 15.5.4.4
64function StringCharAt(pos) {
lrn@chromium.org1c092762011-05-09 09:42:16 +000065 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
66 throw MakeTypeError("called_on_null_or_undefined",
67 ["String.prototype.charAt"]);
68 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000069 var result = %_StringCharAt(this, pos);
70 if (%_IsSmi(result)) {
71 result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
ager@chromium.org381abbb2009-02-25 13:23:22 +000072 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000073 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000074}
75
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000076
77// ECMA-262 section 15.5.4.5
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000078function StringCharCodeAt(pos) {
lrn@chromium.org1c092762011-05-09 09:42:16 +000079 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
80 throw MakeTypeError("called_on_null_or_undefined",
81 ["String.prototype.charCodeAt"]);
82 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000083 var result = %_StringCharCodeAt(this, pos);
84 if (!%_IsSmi(result)) {
85 result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
kasper.lund7276f142008-07-30 08:49:36 +000086 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +000087 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000088}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000089
90
91// ECMA-262, section 15.5.4.6
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000092function StringConcat() {
lrn@chromium.org1c092762011-05-09 09:42:16 +000093 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +000094 throw MakeTypeError("called_on_null_or_undefined",
95 ["String.prototype.concat"]);
lrn@chromium.org1c092762011-05-09 09:42:16 +000096 }
ager@chromium.org5c838252010-02-19 08:53:10 +000097 var len = %_ArgumentsLength();
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +000098 var this_as_string = TO_STRING_INLINE(this);
ager@chromium.org5c838252010-02-19 08:53:10 +000099 if (len === 1) {
100 return this_as_string + %_Arguments(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000101 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000102 var parts = new InternalArray(len + 1);
ager@chromium.org5c838252010-02-19 08:53:10 +0000103 parts[0] = this_as_string;
104 for (var i = 0; i < len; i++) {
105 var part = %_Arguments(i);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000106 parts[i + 1] = TO_STRING_INLINE(part);
ager@chromium.org5c838252010-02-19 08:53:10 +0000107 }
108 return %StringBuilderConcat(parts, len + 1, "");
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000109}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000110
111// Match ES3 and Safari
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000112%FunctionSetLength(StringConcat, 1);
113
114
115// ECMA-262 section 15.5.4.7
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000116function StringIndexOf(pattern /* position */) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000117 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
118 throw MakeTypeError("called_on_null_or_undefined",
119 ["String.prototype.indexOf"]);
120 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000121 var subject = TO_STRING_INLINE(this);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000122 pattern = TO_STRING_INLINE(pattern);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000123 var index = 0;
124 if (%_ArgumentsLength() > 1) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000125 index = %_Arguments(1); // position
126 index = TO_INTEGER(index);
127 if (index < 0) index = 0;
128 if (index > subject.length) index = subject.length;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000129 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000130 return %StringIndexOf(subject, pattern, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000131}
132
133
134// ECMA-262 section 15.5.4.8
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000135function StringLastIndexOf(pat /* position */) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000136 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
137 throw MakeTypeError("called_on_null_or_undefined",
138 ["String.prototype.lastIndexOf"]);
139 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000140 var sub = TO_STRING_INLINE(this);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000141 var subLength = sub.length;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000142 var pat = TO_STRING_INLINE(pat);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000143 var patLength = pat.length;
144 var index = subLength - patLength;
145 if (%_ArgumentsLength() > 1) {
146 var position = ToNumber(%_Arguments(1));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000147 if (!NUMBER_IS_NAN(position)) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000148 position = TO_INTEGER(position);
149 if (position < 0) {
150 position = 0;
151 }
152 if (position + patLength < subLength) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +0000153 index = position;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000154 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000155 }
156 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000157 if (index < 0) {
158 return -1;
159 }
160 return %StringLastIndexOf(sub, pat, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000161}
162
163
164// ECMA-262 section 15.5.4.9
165//
166// This function is implementation specific. For now, we do not
167// do anything locale specific.
168function StringLocaleCompare(other) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000169 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
170 throw MakeTypeError("called_on_null_or_undefined",
171 ["String.prototype.localeCompare"]);
172 }
rossberg@chromium.org28a37082011-08-22 11:03:23 +0000173 return %StringLocaleCompare(TO_STRING_INLINE(this),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000174 TO_STRING_INLINE(other));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000175}
176
177
178// ECMA-262 section 15.5.4.10
179function StringMatch(regexp) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000180 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
181 throw MakeTypeError("called_on_null_or_undefined",
182 ["String.prototype.match"]);
183 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000184 var subject = TO_STRING_INLINE(this);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000185 if (IS_REGEXP(regexp)) {
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000186 // Emulate RegExp.prototype.exec's side effect in step 5, even though
187 // value is discarded.
mstarzinger@chromium.orge0e1b0d2013-07-08 08:38:06 +0000188 var lastIndex = regexp.lastIndex;
189 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000190 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000191 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
192 // lastMatchInfo is defined in regexp.js.
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000193 var result = %StringMatch(subject, regexp, lastMatchInfo);
194 if (result !== null) lastMatchInfoOverride = null;
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000195 regexp.lastIndex = 0;
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000196 return result;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000197 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000198 // Non-regexp argument.
199 regexp = new $RegExp(regexp);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000200 return RegExpExecNoTests(regexp, subject, 0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000201}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000202
203
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000204// This has the same size as the lastMatchInfo array, and can be used for
205// functions that expect that structure to be returned. It is used when the
206// needle is a string rather than a regexp. In this case we can't update
207// lastMatchArray without erroneously affecting the properties on the global
208// RegExp object.
209var reusableMatchInfo = [2, "", "", -1, -1];
210
211
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000212// ECMA-262, section 15.5.4.11
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000213function StringReplace(search, replace) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000214 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
215 throw MakeTypeError("called_on_null_or_undefined",
216 ["String.prototype.replace"]);
217 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000218 var subject = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000219
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000220 // Decision tree for dispatch
221 // .. regexp search
222 // .... string replace
223 // ...... non-global search
224 // ........ empty string replace
225 // ........ non-empty string replace (with $-expansion)
226 // ...... global search
227 // ........ no need to circumvent last match info override
228 // ........ need to circument last match info override
229 // .... function replace
230 // ...... global search
231 // ...... non-global search
232 // .. string search
233 // .... special case that replaces with one single character
234 // ...... function replace
235 // ...... string replace (with $-expansion)
236
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000237 if (IS_REGEXP(search)) {
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000238 // Emulate RegExp.prototype.exec's side effect in step 5, even if
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000239 // value is discarded.
mstarzinger@chromium.orge0e1b0d2013-07-08 08:38:06 +0000240 var lastIndex = search.lastIndex;
241 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000242 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000243
244 if (!IS_SPEC_FUNCTION(replace)) {
245 replace = TO_STRING_INLINE(replace);
246
247 if (!search.global) {
248 // Non-global regexp search, string replace.
249 var match = DoRegExpExec(search, subject, 0);
250 if (match == null) {
251 search.lastIndex = 0
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000252 return subject;
253 }
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000254 if (replace.length == 0) {
255 return %_SubString(subject, 0, match[CAPTURE0]) +
256 %_SubString(subject, match[CAPTURE1], subject.length)
257 }
258 return ExpandReplacement(replace, subject, lastMatchInfo,
259 %_SubString(subject, 0, match[CAPTURE0])) +
260 %_SubString(subject, match[CAPTURE1], subject.length);
261 }
262
263 // Global regexp search, string replace.
264 search.lastIndex = 0;
265 if (lastMatchInfoOverride == null) {
266 return %StringReplaceGlobalRegExpWithString(
267 subject, search, replace, lastMatchInfo);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000268 } else {
269 // We use this hack to detect whether StringReplaceRegExpWithString
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000270 // found at least one hit. In that case we need to remove any
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000271 // override.
272 var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX];
273 lastMatchInfo[LAST_SUBJECT_INDEX] = 0;
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000274 var answer = %StringReplaceGlobalRegExpWithString(
275 subject, search, replace, lastMatchInfo);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000276 if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
277 lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
278 } else {
279 lastMatchInfoOverride = null;
280 }
281 return answer;
282 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283 }
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000284
285 if (search.global) {
286 // Global regexp search, function replace.
287 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
288 }
289 // Non-global regexp search, function replace.
290 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 }
292
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000293 search = TO_STRING_INLINE(search);
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000294
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000295 if (search.length == 1 &&
296 subject.length > 0xFF &&
297 IS_STRING(replace) &&
298 %StringIndexOf(replace, '$', 0) < 0) {
299 // Searching by traversing a cons string tree and replace with cons of
300 // slices works only when the replaced string is a single character, being
301 // replaced by a simple string and only pays off for long strings.
302 return %StringReplaceOneCharWithString(subject, search, replace);
303 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000304 var start = %StringIndexOf(subject, search, 0);
305 if (start < 0) return subject;
306 var end = start + search.length;
307
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000308 var result = %_SubString(subject, 0, start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309
310 // Compute the string to replace with.
lrn@chromium.org34e60782011-09-15 07:25:40 +0000311 if (IS_SPEC_FUNCTION(replace)) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000312 var receiver = %GetDefaultReceiver(replace);
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000313 result += %_CallFunction(receiver, search, start, subject, replace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000315 reusableMatchInfo[CAPTURE0] = start;
316 reusableMatchInfo[CAPTURE1] = end;
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000317 result = ExpandReplacement(TO_STRING_INLINE(replace),
318 subject,
319 reusableMatchInfo,
320 result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000321 }
322
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000323 return result + %_SubString(subject, end, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000324}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325
326
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000327// Expand the $-expressions in the string and return a new string with
328// the result.
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000329function ExpandReplacement(string, subject, matchInfo, result) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000330 var length = string.length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331 var next = %StringIndexOf(string, '$', 0);
332 if (next < 0) {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000333 if (length > 0) result += string;
334 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000335 }
336
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000337 if (next > 0) result += %_SubString(string, 0, next);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000338
339 while (true) {
340 var expansion = '$';
341 var position = next + 1;
342 if (position < length) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000343 var peek = %_StringCharCodeAt(string, position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000344 if (peek == 36) { // $$
345 ++position;
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000346 result += '$';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000347 } else if (peek == 38) { // $& - match
348 ++position;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000349 result +=
350 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000351 } else if (peek == 96) { // $` - prefix
352 ++position;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000353 result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000354 } else if (peek == 39) { // $' - suffix
355 ++position;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000356 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000357 } else if (peek >= 48 && peek <= 57) {
358 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
359 var scaled_index = (peek - 48) << 1;
360 var advance = 1;
361 var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
362 if (position + 1 < string.length) {
363 var next = %_StringCharCodeAt(string, position + 1);
364 if (next >= 48 && next <= 57) {
365 var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
366 if (new_scaled_index < number_of_captures) {
367 scaled_index = new_scaled_index;
368 advance = 2;
369 }
370 }
371 }
372 if (scaled_index != 0 && scaled_index < number_of_captures) {
373 var start = matchInfo[CAPTURE(scaled_index)];
374 if (start >= 0) {
375 result +=
376 %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
377 }
378 position += advance;
379 } else {
380 result += '$';
381 }
ager@chromium.org8c51fc92009-04-22 11:54:55 +0000382 } else {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000383 result += '$';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000385 } else {
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000386 result += '$';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387 }
388
ager@chromium.org8bb60582008-12-11 12:02:20 +0000389 // Go the the next $ in the string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000390 next = %StringIndexOf(string, '$', position);
391
392 // Return if there are no more $ characters in the string. If we
393 // haven't reached the end, we need to append the suffix.
394 if (next < 0) {
395 if (position < length) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000396 result += %_SubString(string, position, length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000397 }
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000398 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399 }
400
401 // Append substring between the previous and the next $ character.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000402 if (next > position) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000403 result += %_SubString(string, position, next);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000404 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000405 }
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000406 return result;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000407}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408
409
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000410// Compute the string of a given regular expression capture.
411function CaptureString(string, lastCaptureInfo, index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000412 // Scale the index.
413 var scaled = index << 1;
414 // Compute start and end.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000415 var start = lastCaptureInfo[CAPTURE(scaled)];
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000416 // If start isn't valid, return undefined.
417 if (start < 0) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000418 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000419 return %_SubString(string, start, end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000420}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000421
422
lrn@chromium.org25156de2010-04-06 13:10:27 +0000423// TODO(lrn): This array will survive indefinitely if replace is never
424// called again. However, it will be empty, since the contents are cleared
425// in the finally block.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000426var reusableReplaceArray = new InternalArray(16);
ager@chromium.org8bb60582008-12-11 12:02:20 +0000427
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000428// Helper function for replacing regular expressions with the result of a
lrn@chromium.org25156de2010-04-06 13:10:27 +0000429// function application in String.prototype.replace.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000430function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
431 var resultArray = reusableReplaceArray;
432 if (resultArray) {
433 reusableReplaceArray = null;
434 } else {
435 // Inside a nested replace (replace called from the replacement function
436 // of another replace) or we have failed to set the reusable array
437 // back due to an exception in a replacement function. Create a new
438 // array to use in the future, or until the original is written back.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000439 resultArray = new InternalArray(16);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000440 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000441 var res = %RegExpExecMultiple(regexp,
442 subject,
443 lastMatchInfo,
444 resultArray);
445 regexp.lastIndex = 0;
446 if (IS_NULL(res)) {
447 // No matches at all.
448 reusableReplaceArray = resultArray;
449 return subject;
450 }
451 var len = res.length;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000452 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000453 // If the number of captures is two then there are no explicit captures in
454 // the regexp, just the implicit capture that captures the whole match. In
455 // this case we can simplify quite a bit and end up with something faster.
456 // The builder will consist of some integers that indicate slices of the
457 // input string and some replacements that were returned from the replace
458 // function.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000459 var match_start = 0;
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000460 var override = new InternalPackedArray(null, 0, subject);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000461 var receiver = %GetDefaultReceiver(replace);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000462 for (var i = 0; i < len; i++) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000463 var elem = res[i];
464 if (%_IsSmi(elem)) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000465 // Integers represent slices of the original string. Use these to
466 // get the offsets we need for the override array (so things like
467 // RegExp.leftContext work during the callback function.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000468 if (elem > 0) {
469 match_start = (elem >> 11) + (elem & 0x7ff);
470 } else {
471 match_start = res[++i] - elem;
472 }
473 } else {
474 override[0] = elem;
475 override[1] = match_start;
476 lastMatchInfoOverride = override;
477 var func_result =
478 %_CallFunction(receiver, elem, match_start, subject, replace);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000479 // Overwrite the i'th element in the results with the string we got
480 // back from the callback function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000481 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000482 match_start += elem.length;
483 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000484 }
485 } else {
lrn@chromium.org34e60782011-09-15 07:25:40 +0000486 var receiver = %GetDefaultReceiver(replace);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000487 for (var i = 0; i < len; i++) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000488 var elem = res[i];
489 if (!%_IsSmi(elem)) {
490 // elem must be an Array.
491 // Use the apply argument as backing for global RegExp properties.
492 lastMatchInfoOverride = elem;
lrn@chromium.org34e60782011-09-15 07:25:40 +0000493 var func_result = %Apply(replace, receiver, elem, 0, elem.length);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000494 // Overwrite the i'th element in the results with the string we got
495 // back from the callback function.
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000496 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000497 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000498 }
499 }
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000500 var result = %StringBuilderConcat(res, res.length, subject);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000501 resultArray.length = 0;
502 reusableReplaceArray = resultArray;
503 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000504}
505
506
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000507function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
508 var matchInfo = DoRegExpExec(regexp, subject, 0);
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000509 if (IS_NULL(matchInfo)) {
510 regexp.lastIndex = 0;
511 return subject;
512 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000513 var index = matchInfo[CAPTURE0];
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000514 var result = %_SubString(subject, 0, index);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000515 var endOfMatch = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516 // Compute the parameter list consisting of the match, captures, index,
517 // and subject for the replace function invocation.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000518 // The number of captures plus one for the match.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000519 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000520 var replacement;
lrn@chromium.org34e60782011-09-15 07:25:40 +0000521 var receiver = %GetDefaultReceiver(replace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522 if (m == 1) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000523 // No captures, only the match, which is always valid.
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000524 var s = %_SubString(subject, index, endOfMatch);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000525 // Don't call directly to avoid exposing the built-in global object.
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +0000526 replacement = %_CallFunction(receiver, s, index, subject, replace);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000527 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000528 var parameters = new InternalArray(m + 2);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000529 for (var j = 0; j < m; j++) {
530 parameters[j] = CaptureString(subject, matchInfo, j);
531 }
532 parameters[j] = index;
533 parameters[j + 1] = subject;
534
lrn@chromium.org34e60782011-09-15 07:25:40 +0000535 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000536 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000537
danno@chromium.org1044a4d2012-04-30 12:34:39 +0000538 result += replacement; // The add method converts to string if necessary.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000539 // Can't use matchInfo any more from here, since the function could
540 // overwrite it.
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000541 return result + %_SubString(subject, endOfMatch, subject.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000542}
543
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000544
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000545// ECMA-262 section 15.5.4.12
lrn@chromium.org25156de2010-04-06 13:10:27 +0000546function StringSearch(re) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000547 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
548 throw MakeTypeError("called_on_null_or_undefined",
549 ["String.prototype.search"]);
550 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000551 var regexp;
552 if (IS_STRING(re)) {
553 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
554 } else if (IS_REGEXP(re)) {
555 regexp = re;
556 } else {
557 regexp = new $RegExp(re);
558 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000559 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000560 if (match) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000561 return match[CAPTURE0];
562 }
563 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000564}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000565
566
567// ECMA-262 section 15.5.4.13
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000568function StringSlice(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000569 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
570 throw MakeTypeError("called_on_null_or_undefined",
571 ["String.prototype.slice"]);
572 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000573 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574 var s_len = s.length;
575 var start_i = TO_INTEGER(start);
576 var end_i = s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000577 if (end !== void 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 end_i = TO_INTEGER(end);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000579 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000580
581 if (start_i < 0) {
582 start_i += s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000583 if (start_i < 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584 start_i = 0;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000585 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000586 } else {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000587 if (start_i > s_len) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000588 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000589 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000590 }
591
592 if (end_i < 0) {
593 end_i += s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000594 if (end_i < 0) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000595 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000596 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000597 } else {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000598 if (end_i > s_len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000599 end_i = s_len;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000600 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000601 }
602
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000603 if (end_i <= start_i) {
604 return '';
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000605 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000606
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000607 return %_SubString(s, start_i, end_i);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000608}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000609
610
611// ECMA-262 section 15.5.4.14
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000612function StringSplit(separator, limit) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000613 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
614 throw MakeTypeError("called_on_null_or_undefined",
615 ["String.prototype.split"]);
616 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000617 var subject = TO_STRING_INLINE(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000618 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000619
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000620 // ECMA-262 says that if separator is undefined, the result should
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000621 // be an array of size 1 containing the entire string.
622 if (IS_UNDEFINED(separator)) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000623 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000624 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000625
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000626 var length = subject.length;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000627 if (!IS_REGEXP(separator)) {
628 separator = TO_STRING_INLINE(separator);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000629
630 if (limit === 0) return [];
631
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000632 var separator_length = separator.length;
633
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000634 // If the separator string is empty then return the elements in the subject.
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000635 if (separator_length === 0) return %StringToArray(subject, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000636
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000637 var result = %StringSplit(subject, separator, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000638
639 return result;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000640 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000641
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000642 if (limit === 0) return [];
643
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000644 // Separator is a regular expression.
645 return StringSplitOnRegExp(subject, separator, limit, length);
646}
647
648
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000649var ArrayPushBuiltin = $Array.prototype.push;
650
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +0000651function StringSplitOnRegExp(subject, separator, limit, length) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000652 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
653
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000654 if (length === 0) {
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000655 if (DoRegExpExec(separator, subject, 0, 0) != null) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000656 return [];
657 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000658 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000659 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000660
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000661 var currentIndex = 0;
662 var startIndex = 0;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000663 var startMatch = 0;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000664 var result = [];
665
ager@chromium.org357bf652010-04-12 11:30:10 +0000666 outer_loop:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000667 while (true) {
668
669 if (startIndex === length) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000670 %_CallFunction(result, %_SubString(subject, currentIndex, length),
671 ArrayPushBuiltin);
ager@chromium.org357bf652010-04-12 11:30:10 +0000672 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000673 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000674
whesse@chromium.org023421e2010-12-21 12:19:12 +0000675 var matchInfo = DoRegExpExec(separator, subject, startIndex);
676 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000677 %_CallFunction(result, %_SubString(subject, currentIndex, length),
678 ArrayPushBuiltin);
ager@chromium.org357bf652010-04-12 11:30:10 +0000679 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000680 }
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000681 var endIndex = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000682
683 // We ignore a zero-length match at the currentIndex.
684 if (startIndex === endIndex && endIndex === currentIndex) {
685 startIndex++;
686 continue;
687 }
688
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000689 %_CallFunction(result, %_SubString(subject, currentIndex, startMatch),
690 ArrayPushBuiltin);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000691
ager@chromium.org357bf652010-04-12 11:30:10 +0000692 if (result.length === limit) break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000693
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000694 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
695 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
696 var start = matchInfo[i++];
697 var end = matchInfo[i++];
698 if (end != -1) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000699 %_CallFunction(result, %_SubString(subject, start, end),
700 ArrayPushBuiltin);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000701 } else {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +0000702 %_CallFunction(result, void 0, ArrayPushBuiltin);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000703 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000704 if (result.length === limit) break outer_loop;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000706
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000707 startIndex = currentIndex = endIndex;
708 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000709 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000710}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000711
712
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713// ECMA-262 section 15.5.4.15
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000714function StringSubstring(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000715 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
716 throw MakeTypeError("called_on_null_or_undefined",
717 ["String.prototype.subString"]);
718 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000719 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720 var s_len = s.length;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000721
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000722 var start_i = TO_INTEGER(start);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000723 if (start_i < 0) {
724 start_i = 0;
725 } else if (start_i > s_len) {
726 start_i = s_len;
727 }
728
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 var end_i = s_len;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000730 if (!IS_UNDEFINED(end)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731 end_i = TO_INTEGER(end);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000732 if (end_i > s_len) {
733 end_i = s_len;
734 } else {
735 if (end_i < 0) end_i = 0;
736 if (start_i > end_i) {
737 var tmp = end_i;
738 end_i = start_i;
739 start_i = tmp;
740 }
741 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000742 }
743
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000744 return %_SubString(s, start_i, end_i);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000745}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746
747
748// This is not a part of ECMA-262.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000749function StringSubstr(start, n) {
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.substr"]);
753 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000754 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000755 var len;
756
757 // Correct n: If not given, set to string length; if explicitly
758 // set to undefined, zero, or negative, returns empty string.
759 if (n === void 0) {
760 len = s.length;
761 } else {
762 len = TO_INTEGER(n);
763 if (len <= 0) return '';
764 }
765
766 // Correct start: If not given (or undefined), set to zero; otherwise
767 // convert to integer and handle negative case.
768 if (start === void 0) {
769 start = 0;
770 } else {
771 start = TO_INTEGER(start);
772 // If positive, and greater than or equal to the string length,
773 // return empty string.
774 if (start >= s.length) return '';
775 // If negative and absolute value is larger than the string length,
776 // use zero.
777 if (start < 0) {
778 start += s.length;
779 if (start < 0) start = 0;
780 }
781 }
782
783 var end = start + len;
784 if (end > s.length) end = s.length;
785
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000786 return %_SubString(s, start, end);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000787}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000788
789
790// ECMA-262, 15.5.4.16
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000791function StringToLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000792 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
793 throw MakeTypeError("called_on_null_or_undefined",
794 ["String.prototype.toLowerCase"]);
795 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000796 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000797}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000798
799
800// ECMA-262, 15.5.4.17
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000801function StringToLocaleLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000802 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
803 throw MakeTypeError("called_on_null_or_undefined",
804 ["String.prototype.toLocaleLowerCase"]);
805 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000806 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000807}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000808
809
810// ECMA-262, 15.5.4.18
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000811function StringToUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000812 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
813 throw MakeTypeError("called_on_null_or_undefined",
814 ["String.prototype.toUpperCase"]);
815 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000816 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000817}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818
819
820// ECMA-262, 15.5.4.19
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000821function StringToLocaleUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000822 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
823 throw MakeTypeError("called_on_null_or_undefined",
824 ["String.prototype.toLocaleUpperCase"]);
825 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000826 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000827}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000828
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000829// ES5, 15.5.4.20
830function StringTrim() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000831 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
832 throw MakeTypeError("called_on_null_or_undefined",
833 ["String.prototype.trim"]);
834 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000835 return %StringTrim(TO_STRING_INLINE(this), true, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000836}
837
838function StringTrimLeft() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000839 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
840 throw MakeTypeError("called_on_null_or_undefined",
841 ["String.prototype.trimLeft"]);
842 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000843 return %StringTrim(TO_STRING_INLINE(this), true, false);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000844}
845
846function StringTrimRight() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000847 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
848 throw MakeTypeError("called_on_null_or_undefined",
849 ["String.prototype.trimRight"]);
850 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000851 return %StringTrim(TO_STRING_INLINE(this), false, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000852}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000853
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000854
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855// ECMA-262, section 15.5.3.2
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000856function StringFromCharCode(code) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000857 var n = %_ArgumentsLength();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000858 if (n == 1) {
859 if (!%_IsSmi(code)) code = ToNumber(code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000860 return %_StringCharFromCode(code & 0xffff);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000861 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000862
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000863 var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
864 var i;
865 for (i = 0; i < n; i++) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000866 var code = %_Arguments(i);
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000867 if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000868 if (code < 0) code = code & 0xffff;
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000869 if (code > 0xff) break;
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000870 %_OneByteSeqStringSetChar(one_byte, i, code);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000871 }
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000872 if (i == n) return one_byte;
873 one_byte = %TruncateString(one_byte, i);
874
875 var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING);
876 for (var j = 0; i < n; i++, j++) {
877 var code = %_Arguments(i);
878 if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
879 %_TwoByteSeqStringSetChar(two_byte, j, code);
880 }
881 return one_byte + two_byte;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000882}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000883
884
885// Helper function for very basic XSS protection.
886function HtmlEscape(str) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000887 return TO_STRING_INLINE(str).replace(/</g, "&lt;")
888 .replace(/>/g, "&gt;")
889 .replace(/"/g, "&quot;")
890 .replace(/'/g, "&#039;");
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000891}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000892
893
894// Compatibility support for KJS.
895// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000896function StringLink(s) {
897 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
898}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899
900
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000901function StringAnchor(name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
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 StringFontcolor(color) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000907 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
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 StringFontsize(size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000912 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
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 StringBig() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000917 return "<big>" + this + "</big>";
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 StringBlink() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000922 return "<blink>" + this + "</blink>";
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 StringBold() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000927 return "<b>" + this + "</b>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000928}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000929
930
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000931function StringFixed() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000932 return "<tt>" + this + "</tt>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000933}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000934
935
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000936function StringItalics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000937 return "<i>" + this + "</i>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000938}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939
940
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000941function StringSmall() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942 return "<small>" + this + "</small>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000943}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944
945
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000946function StringStrike() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000947 return "<strike>" + this + "</strike>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000948}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000949
950
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000951function StringSub() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000952 return "<sub>" + this + "</sub>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000953}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000954
955
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000956function StringSup() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000957 return "<sup>" + this + "</sup>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000958}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000960// -------------------------------------------------------------------
961
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000962function SetUpString() {
963 %CheckIsBootstrapping();
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000964
965 // Set the String function and constructor.
966 %SetCode($String, StringConstructor);
967 %FunctionSetPrototype($String, new $String());
968
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000969 // Set up the constructor property on the String prototype object.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000970 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
971
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000972 // Set up the non-enumerable functions on the String object.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000973 InstallFunctions($String, DONT_ENUM, $Array(
974 "fromCharCode", StringFromCharCode
975 ));
976
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000977 // Set up the non-enumerable functions on the String prototype object.
ricow@chromium.org27bf2882011-11-17 08:34:43 +0000978 InstallFunctions($String.prototype, DONT_ENUM, $Array(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000979 "valueOf", StringValueOf,
980 "toString", StringToString,
981 "charAt", StringCharAt,
982 "charCodeAt", StringCharCodeAt,
983 "concat", StringConcat,
984 "indexOf", StringIndexOf,
985 "lastIndexOf", StringLastIndexOf,
986 "localeCompare", StringLocaleCompare,
987 "match", StringMatch,
988 "replace", StringReplace,
989 "search", StringSearch,
990 "slice", StringSlice,
991 "split", StringSplit,
992 "substring", StringSubstring,
993 "substr", StringSubstr,
994 "toLowerCase", StringToLowerCase,
995 "toLocaleLowerCase", StringToLocaleLowerCase,
996 "toUpperCase", StringToUpperCase,
997 "toLocaleUpperCase", StringToLocaleUpperCase,
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000998 "trim", StringTrim,
999 "trimLeft", StringTrimLeft,
1000 "trimRight", StringTrimRight,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001001 "link", StringLink,
1002 "anchor", StringAnchor,
1003 "fontcolor", StringFontcolor,
1004 "fontsize", StringFontsize,
1005 "big", StringBig,
1006 "blink", StringBlink,
1007 "bold", StringBold,
1008 "fixed", StringFixed,
1009 "italics", StringItalics,
1010 "small", StringSmall,
1011 "strike", StringStrike,
1012 "sub", StringSub,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001013 "sup", StringSup
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001014 ));
1015}
1016
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001017SetUpString();