blob: 2a5c95e4501775e55a3651190717ecc404e7b791 [file] [log] [blame]
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001// Copyright 2006-2009 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:
31// const $String = global.String;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000032// const $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() {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +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');
51 return %_ValueOf(this);
52}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000054
55// ECMA-262 section 15.5.4.3
56function StringValueOf() {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000057 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000058 throw new $TypeError('String.prototype.valueOf is not generic');
59 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)) {
94 throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
95 }
ager@chromium.org5c838252010-02-19 08:53:10 +000096 var len = %_ArgumentsLength();
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +000097 var this_as_string = TO_STRING_INLINE(this);
ager@chromium.org5c838252010-02-19 08:53:10 +000098 if (len === 1) {
99 return this_as_string + %_Arguments(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000100 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000101 var parts = new InternalArray(len + 1);
ager@chromium.org5c838252010-02-19 08:53:10 +0000102 parts[0] = this_as_string;
103 for (var i = 0; i < len; i++) {
104 var part = %_Arguments(i);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000105 parts[i + 1] = TO_STRING_INLINE(part);
ager@chromium.org5c838252010-02-19 08:53:10 +0000106 }
107 return %StringBuilderConcat(parts, len + 1, "");
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000108}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000109
110// Match ES3 and Safari
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000111%FunctionSetLength(StringConcat, 1);
112
113
114// ECMA-262 section 15.5.4.7
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000115function StringIndexOf(pattern /* position */) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000116 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
117 throw MakeTypeError("called_on_null_or_undefined",
118 ["String.prototype.indexOf"]);
119 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000120 var subject = TO_STRING_INLINE(this);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000121 pattern = TO_STRING_INLINE(pattern);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000122 var index = 0;
123 if (%_ArgumentsLength() > 1) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000124 index = %_Arguments(1); // position
125 index = TO_INTEGER(index);
126 if (index < 0) index = 0;
127 if (index > subject.length) index = subject.length;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000128 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000129 return %StringIndexOf(subject, pattern, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000130}
131
132
133// ECMA-262 section 15.5.4.8
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000134function StringLastIndexOf(pat /* position */) { // length == 1
lrn@chromium.org1c092762011-05-09 09:42:16 +0000135 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
136 throw MakeTypeError("called_on_null_or_undefined",
137 ["String.prototype.lastIndexOf"]);
138 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000139 var sub = TO_STRING_INLINE(this);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000140 var subLength = sub.length;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000141 var pat = TO_STRING_INLINE(pat);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000142 var patLength = pat.length;
143 var index = subLength - patLength;
144 if (%_ArgumentsLength() > 1) {
145 var position = ToNumber(%_Arguments(1));
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000146 if (!NUMBER_IS_NAN(position)) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000147 position = TO_INTEGER(position);
148 if (position < 0) {
149 position = 0;
150 }
151 if (position + patLength < subLength) {
152 index = position
153 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000154 }
155 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000156 if (index < 0) {
157 return -1;
158 }
159 return %StringLastIndexOf(sub, pat, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000160}
161
162
163// ECMA-262 section 15.5.4.9
164//
165// This function is implementation specific. For now, we do not
166// do anything locale specific.
167function StringLocaleCompare(other) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000168 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
169 throw MakeTypeError("called_on_null_or_undefined",
170 ["String.prototype.localeCompare"]);
171 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000172 if (%_ArgumentsLength() === 0) return 0;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000173 return %StringLocaleCompare(TO_STRING_INLINE(this),
174 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)) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000186 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000187 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
188 // lastMatchInfo is defined in regexp.js.
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000189 return %StringMatch(subject, regexp, lastMatchInfo);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000190 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000191 // Non-regexp argument.
192 regexp = new $RegExp(regexp);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000193 return RegExpExecNoTests(regexp, subject, 0);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000194}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000195
196
197// SubString is an internal function that returns the sub string of 'string'.
198// If resulting string is of length 1, we use the one character cache
199// otherwise we call the runtime system.
200function SubString(string, start, end) {
201 // Use the one character string cache.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000202 if (start + 1 == end) return %_StringCharAt(string, start);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000203 return %_SubString(string, start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204}
205
206
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000207// This has the same size as the lastMatchInfo array, and can be used for
208// functions that expect that structure to be returned. It is used when the
209// needle is a string rather than a regexp. In this case we can't update
210// lastMatchArray without erroneously affecting the properties on the global
211// RegExp object.
212var reusableMatchInfo = [2, "", "", -1, -1];
213
214
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000215// ECMA-262, section 15.5.4.11
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000216function StringReplace(search, replace) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000217 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
218 throw MakeTypeError("called_on_null_or_undefined",
219 ["String.prototype.replace"]);
220 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000221 var subject = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000222
223 // Delegate to one of the regular expression variants if necessary.
224 if (IS_REGEXP(search)) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000225 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000226 if (IS_FUNCTION(replace)) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000227 if (search.global) {
228 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
229 } else {
230 return StringReplaceNonGlobalRegExpWithFunction(subject,
231 search,
232 replace);
233 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000234 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000235 return %StringReplaceRegExpWithString(subject,
236 search,
237 TO_STRING_INLINE(replace),
238 lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000239 }
240 }
241
242 // Convert the search argument to a string and search for it.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000243 search = TO_STRING_INLINE(search);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000244 var start = %StringIndexOf(subject, search, 0);
245 if (start < 0) return subject;
246 var end = start + search.length;
247
ager@chromium.org8bb60582008-12-11 12:02:20 +0000248 var builder = new ReplaceResultBuilder(subject);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000249 // prefix
ager@chromium.org8bb60582008-12-11 12:02:20 +0000250 builder.addSpecialSlice(0, start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000251
252 // Compute the string to replace with.
253 if (IS_FUNCTION(replace)) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000254 var receiver =
255 %_IsNativeOrStrictMode(replace) ? void 0 : %GetGlobalReceiver();
256 builder.add(%_CallFunction(receiver,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000257 search,
258 start,
259 subject,
260 replace));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000261 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000262 reusableMatchInfo[CAPTURE0] = start;
263 reusableMatchInfo[CAPTURE1] = end;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000264 replace = TO_STRING_INLINE(replace);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000265 ExpandReplacement(replace, subject, reusableMatchInfo, builder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 }
267
268 // suffix
ager@chromium.org8bb60582008-12-11 12:02:20 +0000269 builder.addSpecialSlice(end, subject.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270
271 return builder.generate();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000272}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273
274
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000275// Expand the $-expressions in the string and return a new string with
276// the result.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000277function ExpandReplacement(string, subject, matchInfo, builder) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000278 var length = string.length;
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000279 var builder_elements = builder.elements;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000280 var next = %StringIndexOf(string, '$', 0);
281 if (next < 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000282 if (length > 0) builder_elements.push(string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283 return;
284 }
285
286 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000287 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288
whesse@chromium.org023421e2010-12-21 12:19:12 +0000289 if (next > 0) builder_elements.push(SubString(string, 0, next));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290
291 while (true) {
292 var expansion = '$';
293 var position = next + 1;
294 if (position < length) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000295 var peek = %_StringCharCodeAt(string, position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000296 if (peek == 36) { // $$
297 ++position;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000298 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000299 } else if (peek == 38) { // $& - match
300 ++position;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000301 builder.addSpecialSlice(matchInfo[CAPTURE0],
302 matchInfo[CAPTURE1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303 } else if (peek == 96) { // $` - prefix
304 ++position;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000305 builder.addSpecialSlice(0, matchInfo[CAPTURE0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 } else if (peek == 39) { // $' - suffix
307 ++position;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000308 builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
310 ++position;
311 var n = peek - 48;
312 if (position < length) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000313 peek = %_StringCharCodeAt(string, position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 // $nn, 01 <= nn <= 99
315 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
316 var nn = n * 10 + (peek - 48);
317 if (nn < m) {
318 // If the two digit capture reference is within range of
319 // the captures, we use it instead of the single digit
320 // one. Otherwise, we fall back to using the single
321 // digit reference. This matches the behavior of
322 // SpiderMonkey.
323 ++position;
324 n = nn;
325 }
326 }
327 }
328 if (0 < n && n < m) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000329 addCaptureString(builder, matchInfo, n);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330 } else {
331 // Because of the captures range check in the parsing of two
332 // digit capture references, we can only enter here when a
333 // single digit capture reference is outside the range of
334 // captures.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000335 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000336 --position;
337 }
ager@chromium.org8c51fc92009-04-22 11:54:55 +0000338 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000339 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000341 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000342 builder_elements.push('$');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343 }
344
ager@chromium.org8bb60582008-12-11 12:02:20 +0000345 // Go the the next $ in the string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346 next = %StringIndexOf(string, '$', position);
347
348 // Return if there are no more $ characters in the string. If we
349 // haven't reached the end, we need to append the suffix.
350 if (next < 0) {
351 if (position < length) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000352 builder_elements.push(SubString(string, position, length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000353 }
354 return;
355 }
356
357 // Append substring between the previous and the next $ character.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000358 if (next > position) {
359 builder_elements.push(SubString(string, position, next));
360 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000361 }
362};
363
364
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000365// Compute the string of a given regular expression capture.
366function CaptureString(string, lastCaptureInfo, index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000367 // Scale the index.
368 var scaled = index << 1;
369 // Compute start and end.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000370 var start = lastCaptureInfo[CAPTURE(scaled)];
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000371 // If start isn't valid, return undefined.
372 if (start < 0) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000373 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000374 return SubString(string, start, end);
375};
376
377
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000378// Add the string of a given regular expression capture to the
379// ReplaceResultBuilder
380function addCaptureString(builder, matchInfo, index) {
ager@chromium.org8bb60582008-12-11 12:02:20 +0000381 // Scale the index.
382 var scaled = index << 1;
383 // Compute start and end.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000384 var start = matchInfo[CAPTURE(scaled)];
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000385 if (start < 0) return;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000386 var end = matchInfo[CAPTURE(scaled + 1)];
ager@chromium.org8bb60582008-12-11 12:02:20 +0000387 builder.addSpecialSlice(start, end);
388};
389
lrn@chromium.org25156de2010-04-06 13:10:27 +0000390// TODO(lrn): This array will survive indefinitely if replace is never
391// called again. However, it will be empty, since the contents are cleared
392// in the finally block.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000393var reusableReplaceArray = new InternalArray(16);
ager@chromium.org8bb60582008-12-11 12:02:20 +0000394
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000395// Helper function for replacing regular expressions with the result of a
lrn@chromium.org25156de2010-04-06 13:10:27 +0000396// function application in String.prototype.replace.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000397function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
398 var resultArray = reusableReplaceArray;
399 if (resultArray) {
400 reusableReplaceArray = null;
401 } else {
402 // Inside a nested replace (replace called from the replacement function
403 // of another replace) or we have failed to set the reusable array
404 // back due to an exception in a replacement function. Create a new
405 // array to use in the future, or until the original is written back.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000406 resultArray = new InternalArray(16);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000408 var res = %RegExpExecMultiple(regexp,
409 subject,
410 lastMatchInfo,
411 resultArray);
412 regexp.lastIndex = 0;
413 if (IS_NULL(res)) {
414 // No matches at all.
415 reusableReplaceArray = resultArray;
416 return subject;
417 }
418 var len = res.length;
419 var i = 0;
420 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
421 var match_start = 0;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000422 var override = new InternalArray(null, 0, subject);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000423 var receiver =
424 %_IsNativeOrStrictMode(replace) ? void 0 : %GetGlobalReceiver();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000425 while (i < len) {
426 var elem = res[i];
427 if (%_IsSmi(elem)) {
428 if (elem > 0) {
429 match_start = (elem >> 11) + (elem & 0x7ff);
430 } else {
431 match_start = res[++i] - elem;
432 }
433 } else {
434 override[0] = elem;
435 override[1] = match_start;
436 lastMatchInfoOverride = override;
437 var func_result =
438 %_CallFunction(receiver, elem, match_start, subject, replace);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000439 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000440 match_start += elem.length;
441 }
442 i++;
443 }
444 } else {
445 while (i < len) {
446 var elem = res[i];
447 if (!%_IsSmi(elem)) {
448 // elem must be an Array.
449 // Use the apply argument as backing for global RegExp properties.
450 lastMatchInfoOverride = elem;
451 var func_result = replace.apply(null, elem);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +0000452 res[i] = TO_STRING_INLINE(func_result);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000453 }
454 i++;
455 }
456 }
457 var resultBuilder = new ReplaceResultBuilder(subject, res);
458 var result = resultBuilder.generate();
459 resultArray.length = 0;
460 reusableReplaceArray = resultArray;
461 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000462}
463
464
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000465function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
466 var matchInfo = DoRegExpExec(regexp, subject, 0);
467 if (IS_NULL(matchInfo)) return subject;
468 var result = new ReplaceResultBuilder(subject);
469 var index = matchInfo[CAPTURE0];
470 result.addSpecialSlice(0, index);
471 var endOfMatch = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000472 // Compute the parameter list consisting of the match, captures, index,
473 // and subject for the replace function invocation.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000474 // The number of captures plus one for the match.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000475 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000476 var replacement;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000477 if (m == 1) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000478 // No captures, only the match, which is always valid.
479 var s = SubString(subject, index, endOfMatch);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000480 // Don't call directly to avoid exposing the built-in global object.
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000481 var receiver =
482 %_IsNativeOrStrictMode(replace) ? void 0 : %GetGlobalReceiver();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000483 replacement =
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000484 %_CallFunction(receiver, s, index, subject, replace);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000485 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000486 var parameters = new InternalArray(m + 2);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000487 for (var j = 0; j < m; j++) {
488 parameters[j] = CaptureString(subject, matchInfo, j);
489 }
490 parameters[j] = index;
491 parameters[j + 1] = subject;
492
493 replacement = replace.apply(null, parameters);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000494 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000495
496 result.add(replacement); // The add method converts to string if necessary.
497 // Can't use matchInfo any more from here, since the function could
498 // overwrite it.
499 result.addSpecialSlice(endOfMatch, subject.length);
500 return result.generate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000501}
502
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000503
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000504// ECMA-262 section 15.5.4.12
lrn@chromium.org25156de2010-04-06 13:10:27 +0000505function StringSearch(re) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000506 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
507 throw MakeTypeError("called_on_null_or_undefined",
508 ["String.prototype.search"]);
509 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000510 var regexp;
511 if (IS_STRING(re)) {
512 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
513 } else if (IS_REGEXP(re)) {
514 regexp = re;
515 } else {
516 regexp = new $RegExp(re);
517 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000518 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000519 if (match) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +0000520 return match[CAPTURE0];
521 }
522 return -1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000523}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000524
525
526// ECMA-262 section 15.5.4.13
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000527function StringSlice(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000528 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
529 throw MakeTypeError("called_on_null_or_undefined",
530 ["String.prototype.slice"]);
531 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000532 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533 var s_len = s.length;
534 var start_i = TO_INTEGER(start);
535 var end_i = s_len;
536 if (end !== void 0)
537 end_i = TO_INTEGER(end);
538
539 if (start_i < 0) {
540 start_i += s_len;
541 if (start_i < 0)
542 start_i = 0;
543 } else {
544 if (start_i > s_len)
545 start_i = s_len;
546 }
547
548 if (end_i < 0) {
549 end_i += s_len;
550 if (end_i < 0)
551 end_i = 0;
552 } else {
553 if (end_i > s_len)
554 end_i = s_len;
555 }
556
557 var num_c = end_i - start_i;
558 if (num_c < 0)
559 num_c = 0;
560
561 return SubString(s, start_i, start_i + num_c);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000562}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000563
564
565// ECMA-262 section 15.5.4.14
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000566function StringSplit(separator, limit) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000567 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
568 throw MakeTypeError("called_on_null_or_undefined",
569 ["String.prototype.split"]);
570 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000571 var subject = TO_STRING_INLINE(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000572 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000573 if (limit === 0) return [];
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000574
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000575 // ECMA-262 says that if separator is undefined, the result should
576 // be an array of size 1 containing the entire string. SpiderMonkey
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000577 // and KJS have this behavior only when no separator is given. If
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 // undefined is explicitly given, they convert it to a string and
579 // use that. We do as SpiderMonkey and KJS.
580 if (%_ArgumentsLength() === 0) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000581 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000582 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584 var length = subject.length;
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000585 if (!IS_REGEXP(separator)) {
586 separator = TO_STRING_INLINE(separator);
587 var separator_length = separator.length;
588
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000589 // If the separator string is empty then return the elements in the subject.
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000590 if (separator_length === 0) return %StringToArray(subject, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000591
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000592 var result = %StringSplit(subject, separator, limit);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000593
594 return result;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000595 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000596
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000597 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
598
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000599 if (length === 0) {
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +0000600 if (DoRegExpExec(separator, subject, 0, 0) != null) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000601 return [];
602 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000603 return [subject];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000604 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000605
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000606 var currentIndex = 0;
607 var startIndex = 0;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000608 var startMatch = 0;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000609 var result = [];
610
ager@chromium.org357bf652010-04-12 11:30:10 +0000611 outer_loop:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 while (true) {
613
614 if (startIndex === length) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000615 result.push(SubString(subject, currentIndex, length));
ager@chromium.org357bf652010-04-12 11:30:10 +0000616 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000617 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000618
whesse@chromium.org023421e2010-12-21 12:19:12 +0000619 var matchInfo = DoRegExpExec(separator, subject, startIndex);
620 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
621 result.push(SubString(subject, currentIndex, length));
ager@chromium.org357bf652010-04-12 11:30:10 +0000622 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000623 }
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000624 var endIndex = matchInfo[CAPTURE1];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000625
626 // We ignore a zero-length match at the currentIndex.
627 if (startIndex === endIndex && endIndex === currentIndex) {
628 startIndex++;
629 continue;
630 }
631
whesse@chromium.org023421e2010-12-21 12:19:12 +0000632 if (currentIndex + 1 == startMatch) {
633 result.push(%_StringCharAt(subject, currentIndex));
634 } else {
635 result.push(%_SubString(subject, currentIndex, startMatch));
636 }
637
ager@chromium.org357bf652010-04-12 11:30:10 +0000638 if (result.length === limit) break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000639
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000640 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
641 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
642 var start = matchInfo[i++];
643 var end = matchInfo[i++];
644 if (end != -1) {
645 if (start + 1 == end) {
646 result.push(%_StringCharAt(subject, start));
647 } else {
648 result.push(%_SubString(subject, start, end));
649 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000650 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000651 result.push(void 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000652 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000653 if (result.length === limit) break outer_loop;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000654 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000655
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 startIndex = currentIndex = endIndex;
657 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000658 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000659}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000660
661
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000662// ECMA-262 section 15.5.4.15
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000663function StringSubstring(start, end) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000664 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
665 throw MakeTypeError("called_on_null_or_undefined",
666 ["String.prototype.subString"]);
667 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000668 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000669 var s_len = s.length;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000671 var start_i = TO_INTEGER(start);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000672 if (start_i < 0) {
673 start_i = 0;
674 } else if (start_i > s_len) {
675 start_i = s_len;
676 }
677
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000678 var end_i = s_len;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000679 if (!IS_UNDEFINED(end)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000680 end_i = TO_INTEGER(end);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000681 if (end_i > s_len) {
682 end_i = s_len;
683 } else {
684 if (end_i < 0) end_i = 0;
685 if (start_i > end_i) {
686 var tmp = end_i;
687 end_i = start_i;
688 start_i = tmp;
689 }
690 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 }
692
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000693 return (start_i + 1 == end_i
694 ? %_StringCharAt(s, start_i)
695 : %_SubString(s, start_i, end_i));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000696}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697
698
699// This is not a part of ECMA-262.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000700function StringSubstr(start, n) {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000701 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
702 throw MakeTypeError("called_on_null_or_undefined",
703 ["String.prototype.substr"]);
704 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000705 var s = TO_STRING_INLINE(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706 var len;
707
708 // Correct n: If not given, set to string length; if explicitly
709 // set to undefined, zero, or negative, returns empty string.
710 if (n === void 0) {
711 len = s.length;
712 } else {
713 len = TO_INTEGER(n);
714 if (len <= 0) return '';
715 }
716
717 // Correct start: If not given (or undefined), set to zero; otherwise
718 // convert to integer and handle negative case.
719 if (start === void 0) {
720 start = 0;
721 } else {
722 start = TO_INTEGER(start);
723 // If positive, and greater than or equal to the string length,
724 // return empty string.
725 if (start >= s.length) return '';
726 // If negative and absolute value is larger than the string length,
727 // use zero.
728 if (start < 0) {
729 start += s.length;
730 if (start < 0) start = 0;
731 }
732 }
733
734 var end = start + len;
735 if (end > s.length) end = s.length;
736
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000737 return (start + 1 == end
738 ? %_StringCharAt(s, start)
739 : %_SubString(s, start, end));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000740}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741
742
743// ECMA-262, 15.5.4.16
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000744function StringToLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000745 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
746 throw MakeTypeError("called_on_null_or_undefined",
747 ["String.prototype.toLowerCase"]);
748 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000749 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000750}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000751
752
753// ECMA-262, 15.5.4.17
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000754function StringToLocaleLowerCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000755 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
756 throw MakeTypeError("called_on_null_or_undefined",
757 ["String.prototype.toLocaleLowerCase"]);
758 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000759 return %StringToLowerCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000760}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761
762
763// ECMA-262, 15.5.4.18
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000764function StringToUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000765 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
766 throw MakeTypeError("called_on_null_or_undefined",
767 ["String.prototype.toUpperCase"]);
768 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000769 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000770}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000771
772
773// ECMA-262, 15.5.4.19
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000774function StringToLocaleUpperCase() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000775 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
776 throw MakeTypeError("called_on_null_or_undefined",
777 ["String.prototype.toLocaleUpperCase"]);
778 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000779 return %StringToUpperCase(TO_STRING_INLINE(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000780}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000781
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000782// ES5, 15.5.4.20
783function StringTrim() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000784 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
785 throw MakeTypeError("called_on_null_or_undefined",
786 ["String.prototype.trim"]);
787 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000788 return %StringTrim(TO_STRING_INLINE(this), true, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000789}
790
791function StringTrimLeft() {
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.trimLeft"]);
795 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000796 return %StringTrim(TO_STRING_INLINE(this), true, false);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000797}
798
799function StringTrimRight() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000800 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
801 throw MakeTypeError("called_on_null_or_undefined",
802 ["String.prototype.trimRight"]);
803 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000804 return %StringTrim(TO_STRING_INLINE(this), false, true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000805}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000806
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000807var static_charcode_array = new InternalArray(4);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000808
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000809// ECMA-262, section 15.5.3.2
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000810function StringFromCharCode(code) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000811 var n = %_ArgumentsLength();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000812 if (n == 1) {
813 if (!%_IsSmi(code)) code = ToNumber(code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000814 return %_StringCharFromCode(code & 0xffff);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000815 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000816
817 // NOTE: This is not super-efficient, but it is necessary because we
818 // want to avoid converting to numbers from within the virtual
819 // machine. Maybe we can find another way of doing this?
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000820 var codes = static_charcode_array;
821 for (var i = 0; i < n; i++) {
822 var code = %_Arguments(i);
823 if (!%_IsSmi(code)) code = ToNumber(code);
824 codes[i] = code;
825 }
826 codes.length = n;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 return %StringFromCharCodeArray(codes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000828}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829
830
831// Helper function for very basic XSS protection.
832function HtmlEscape(str) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000833 return TO_STRING_INLINE(str).replace(/</g, "&lt;")
834 .replace(/>/g, "&gt;")
835 .replace(/"/g, "&quot;")
836 .replace(/'/g, "&#039;");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837};
838
839
840// Compatibility support for KJS.
841// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000842function StringLink(s) {
843 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
844}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000845
846
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000847function StringAnchor(name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000848 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000849}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850
851
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000852function StringFontcolor(color) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000853 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000854}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855
856
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000857function StringFontsize(size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000858 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000859}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860
861
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000862function StringBig() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000863 return "<big>" + this + "</big>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000864}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000865
866
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000867function StringBlink() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868 return "<blink>" + this + "</blink>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000869}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000870
871
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000872function StringBold() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000873 return "<b>" + this + "</b>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000874}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000875
876
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000877function StringFixed() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 return "<tt>" + this + "</tt>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000879}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000880
881
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000882function StringItalics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000883 return "<i>" + this + "</i>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000884}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
886
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000887function StringSmall() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000888 return "<small>" + this + "</small>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000889}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890
891
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000892function StringStrike() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893 return "<strike>" + this + "</strike>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000894}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000895
896
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000897function StringSub() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000898 return "<sub>" + this + "</sub>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000899}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000900
901
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000902function StringSup() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000903 return "<sup>" + this + "</sup>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000904}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000905
906
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000907// ReplaceResultBuilder support.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000908function ReplaceResultBuilder(str) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000909 if (%_ArgumentsLength() > 1) {
910 this.elements = %_Arguments(1);
911 } else {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000912 this.elements = new InternalArray();
lrn@chromium.org25156de2010-04-06 13:10:27 +0000913 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914 this.special_string = str;
915}
916
917
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000918ReplaceResultBuilder.prototype.add = function(str) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000919 str = TO_STRING_INLINE(str);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000920 if (str.length > 0) this.elements.push(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000921}
922
923
924ReplaceResultBuilder.prototype.addSpecialSlice = function(start, end) {
925 var len = end - start;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000926 if (start < 0 || len <= 0) return;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000927 if (start < 0x80000 && len < 0x800) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000928 this.elements.push((start << 11) | len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000929 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000930 // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength,
931 // so -len is a smi.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000932 var elements = this.elements;
933 elements.push(-len);
934 elements.push(start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000935 }
936}
937
938
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939ReplaceResultBuilder.prototype.generate = function() {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000940 var elements = this.elements;
941 return %StringBuilderConcat(elements, elements.length, this.special_string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000943
944
945// -------------------------------------------------------------------
946
947function SetupString() {
948 // Setup the constructor property on the String prototype object.
949 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
950
951
952 // Setup the non-enumerable functions on the String object.
953 InstallFunctions($String, DONT_ENUM, $Array(
954 "fromCharCode", StringFromCharCode
955 ));
956
957
958 // Setup the non-enumerable functions on the String prototype object.
ager@chromium.org9085a012009-05-11 19:22:57 +0000959 InstallFunctionsOnHiddenPrototype($String.prototype, DONT_ENUM, $Array(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000960 "valueOf", StringValueOf,
961 "toString", StringToString,
962 "charAt", StringCharAt,
963 "charCodeAt", StringCharCodeAt,
964 "concat", StringConcat,
965 "indexOf", StringIndexOf,
966 "lastIndexOf", StringLastIndexOf,
967 "localeCompare", StringLocaleCompare,
968 "match", StringMatch,
969 "replace", StringReplace,
970 "search", StringSearch,
971 "slice", StringSlice,
972 "split", StringSplit,
973 "substring", StringSubstring,
974 "substr", StringSubstr,
975 "toLowerCase", StringToLowerCase,
976 "toLocaleLowerCase", StringToLocaleLowerCase,
977 "toUpperCase", StringToUpperCase,
978 "toLocaleUpperCase", StringToLocaleUpperCase,
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000979 "trim", StringTrim,
980 "trimLeft", StringTrimLeft,
981 "trimRight", StringTrimRight,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000982 "link", StringLink,
983 "anchor", StringAnchor,
984 "fontcolor", StringFontcolor,
985 "fontsize", StringFontsize,
986 "big", StringBig,
987 "blink", StringBlink,
988 "bold", StringBold,
989 "fixed", StringFixed,
990 "italics", StringItalics,
991 "small", StringSmall,
992 "strike", StringStrike,
993 "sub", StringSub,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000994 "sup", StringSup
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000995 ));
996}
997
998
999SetupString();