blob: d8d402c487e85b6d917c31ab440333f6202307d8 [file] [log] [blame]
Steve Blockd0582a62009-12-15 09:54:21 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +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;
32// const $NaN = 0/0;
33
34
35// Set the String function and constructor.
36%SetCode($String, function(x) {
Andrei Popescu402d9372010-02-26 13:31:12 +000037 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
Steve Blocka7e24c12009-10-30 11:49:00 +000038 if (%_IsConstructCall()) {
39 %_SetValueOf(this, value);
40 } else {
41 return value;
42 }
43});
44
45%FunctionSetPrototype($String, new $String());
46
47// ECMA-262 section 15.5.4.2
48function StringToString() {
49 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this))
50 throw new $TypeError('String.prototype.toString is not generic');
51 return %_ValueOf(this);
52}
53
54
55// ECMA-262 section 15.5.4.3
56function StringValueOf() {
57 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this))
58 throw new $TypeError('String.prototype.valueOf is not generic');
59 return %_ValueOf(this);
60}
61
62
63// ECMA-262, section 15.5.4.4
64function StringCharAt(pos) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010065 var result = %_StringCharAt(this, pos);
66 if (%_IsSmi(result)) {
67 result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
Steve Blocka7e24c12009-10-30 11:49:00 +000068 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010069 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +000070}
71
72
73// ECMA-262 section 15.5.4.5
74function StringCharCodeAt(pos) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010075 var result = %_StringCharCodeAt(this, pos);
76 if (!%_IsSmi(result)) {
77 result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
Steve Blocka7e24c12009-10-30 11:49:00 +000078 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010079 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +000080}
81
82
83// ECMA-262, section 15.5.4.6
84function StringConcat() {
Andrei Popescu402d9372010-02-26 13:31:12 +000085 var len = %_ArgumentsLength();
86 var this_as_string = TO_STRING_INLINE(this);
87 if (len === 1) {
88 return this_as_string + %_Arguments(0);
Leon Clarkee46be812010-01-19 14:06:41 +000089 }
Ben Murdoche0cee9b2011-05-25 10:26:03 +010090 var parts = new InternalArray(len + 1);
Andrei Popescu402d9372010-02-26 13:31:12 +000091 parts[0] = this_as_string;
92 for (var i = 0; i < len; i++) {
93 var part = %_Arguments(i);
94 parts[i + 1] = TO_STRING_INLINE(part);
95 }
96 return %StringBuilderConcat(parts, len + 1, "");
Steve Blocka7e24c12009-10-30 11:49:00 +000097}
98
99// Match ES3 and Safari
100%FunctionSetLength(StringConcat, 1);
101
102
103// ECMA-262 section 15.5.4.7
Ben Murdochb0fe1622011-05-05 13:52:32 +0100104function StringIndexOf(pattern /* position */) { // length == 1
105 var subject = TO_STRING_INLINE(this);
Steve Block1e0659c2011-05-24 12:43:12 +0100106 pattern = TO_STRING_INLINE(pattern);
Steve Blocka7e24c12009-10-30 11:49:00 +0000107 var index = 0;
108 if (%_ArgumentsLength() > 1) {
Steve Block1e0659c2011-05-24 12:43:12 +0100109 index = %_Arguments(1); // position
110 index = TO_INTEGER(index);
111 if (index < 0) index = 0;
112 if (index > subject.length) index = subject.length;
Steve Blocka7e24c12009-10-30 11:49:00 +0000113 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100114 return %StringIndexOf(subject, pattern, index);
Steve Blocka7e24c12009-10-30 11:49:00 +0000115}
116
117
118// ECMA-262 section 15.5.4.8
Ben Murdochb0fe1622011-05-05 13:52:32 +0100119function StringLastIndexOf(pat /* position */) { // length == 1
Andrei Popescu402d9372010-02-26 13:31:12 +0000120 var sub = TO_STRING_INLINE(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000121 var subLength = sub.length;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100122 var pat = TO_STRING_INLINE(pat);
Steve Blocka7e24c12009-10-30 11:49:00 +0000123 var patLength = pat.length;
124 var index = subLength - patLength;
125 if (%_ArgumentsLength() > 1) {
126 var position = ToNumber(%_Arguments(1));
Steve Block9fac8402011-05-12 15:51:54 +0100127 if (!NUMBER_IS_NAN(position)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000128 position = TO_INTEGER(position);
129 if (position < 0) {
130 position = 0;
131 }
132 if (position + patLength < subLength) {
133 index = position
134 }
135 }
136 }
137 if (index < 0) {
138 return -1;
139 }
140 return %StringLastIndexOf(sub, pat, index);
141}
142
143
144// ECMA-262 section 15.5.4.9
145//
146// This function is implementation specific. For now, we do not
147// do anything locale specific.
148function StringLocaleCompare(other) {
149 if (%_ArgumentsLength() === 0) return 0;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100150 return %StringLocaleCompare(TO_STRING_INLINE(this),
151 TO_STRING_INLINE(other));
Steve Blocka7e24c12009-10-30 11:49:00 +0000152}
153
154
155// ECMA-262 section 15.5.4.10
156function StringMatch(regexp) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000157 var subject = TO_STRING_INLINE(this);
Steve Block6ded16b2010-05-10 14:33:55 +0100158 if (IS_REGEXP(regexp)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100159 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100160 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
161 // lastMatchInfo is defined in regexp.js.
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800162 return %StringMatch(subject, regexp, lastMatchInfo);
Steve Block6ded16b2010-05-10 14:33:55 +0100163 }
164 // Non-regexp argument.
165 regexp = new $RegExp(regexp);
Steve Block6ded16b2010-05-10 14:33:55 +0100166 return RegExpExecNoTests(regexp, subject, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000167}
168
169
170// SubString is an internal function that returns the sub string of 'string'.
171// If resulting string is of length 1, we use the one character cache
172// otherwise we call the runtime system.
173function SubString(string, start, end) {
174 // Use the one character string cache.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100175 if (start + 1 == end) return %_StringCharAt(string, start);
Leon Clarkee46be812010-01-19 14:06:41 +0000176 return %_SubString(string, start, end);
Steve Blocka7e24c12009-10-30 11:49:00 +0000177}
178
179
180// This has the same size as the lastMatchInfo array, and can be used for
181// functions that expect that structure to be returned. It is used when the
182// needle is a string rather than a regexp. In this case we can't update
183// lastMatchArray without erroneously affecting the properties on the global
184// RegExp object.
185var reusableMatchInfo = [2, "", "", -1, -1];
186
187
188// ECMA-262, section 15.5.4.11
189function StringReplace(search, replace) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000190 var subject = TO_STRING_INLINE(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000191
192 // Delegate to one of the regular expression variants if necessary.
193 if (IS_REGEXP(search)) {
194 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
195 if (IS_FUNCTION(replace)) {
Kristian Monsen25f61362010-05-21 11:50:48 +0100196 if (search.global) {
197 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
198 } else {
199 return StringReplaceNonGlobalRegExpWithFunction(subject,
200 search,
201 replace);
202 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000203 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100204 return %StringReplaceRegExpWithString(subject,
205 search,
206 TO_STRING_INLINE(replace),
207 lastMatchInfo);
Steve Blocka7e24c12009-10-30 11:49:00 +0000208 }
209 }
210
211 // Convert the search argument to a string and search for it.
Andrei Popescu402d9372010-02-26 13:31:12 +0000212 search = TO_STRING_INLINE(search);
Steve Blocka7e24c12009-10-30 11:49:00 +0000213 var start = %StringIndexOf(subject, search, 0);
214 if (start < 0) return subject;
215 var end = start + search.length;
216
217 var builder = new ReplaceResultBuilder(subject);
218 // prefix
219 builder.addSpecialSlice(0, start);
220
221 // Compute the string to replace with.
222 if (IS_FUNCTION(replace)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100223 builder.add(%_CallFunction(%GetGlobalReceiver(),
224 search,
225 start,
226 subject,
227 replace));
Steve Blocka7e24c12009-10-30 11:49:00 +0000228 } else {
229 reusableMatchInfo[CAPTURE0] = start;
230 reusableMatchInfo[CAPTURE1] = end;
Andrei Popescu402d9372010-02-26 13:31:12 +0000231 replace = TO_STRING_INLINE(replace);
Leon Clarkee46be812010-01-19 14:06:41 +0000232 ExpandReplacement(replace, subject, reusableMatchInfo, builder);
Steve Blocka7e24c12009-10-30 11:49:00 +0000233 }
234
235 // suffix
236 builder.addSpecialSlice(end, subject.length);
237
238 return builder.generate();
239}
240
241
Steve Blocka7e24c12009-10-30 11:49:00 +0000242// Expand the $-expressions in the string and return a new string with
243// the result.
244function ExpandReplacement(string, subject, matchInfo, builder) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100245 var length = string.length;
246 var builder_elements = builder.elements;
Steve Blocka7e24c12009-10-30 11:49:00 +0000247 var next = %StringIndexOf(string, '$', 0);
248 if (next < 0) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100249 if (length > 0) builder_elements.push(string);
Steve Blocka7e24c12009-10-30 11:49:00 +0000250 return;
251 }
252
253 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
254 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match.
255
Ben Murdochb0fe1622011-05-05 13:52:32 +0100256 if (next > 0) builder_elements.push(SubString(string, 0, next));
Steve Blocka7e24c12009-10-30 11:49:00 +0000257
258 while (true) {
259 var expansion = '$';
260 var position = next + 1;
261 if (position < length) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100262 var peek = %_StringCharCodeAt(string, position);
Steve Blocka7e24c12009-10-30 11:49:00 +0000263 if (peek == 36) { // $$
264 ++position;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100265 builder_elements.push('$');
Steve Blocka7e24c12009-10-30 11:49:00 +0000266 } else if (peek == 38) { // $& - match
267 ++position;
268 builder.addSpecialSlice(matchInfo[CAPTURE0],
269 matchInfo[CAPTURE1]);
270 } else if (peek == 96) { // $` - prefix
271 ++position;
272 builder.addSpecialSlice(0, matchInfo[CAPTURE0]);
273 } else if (peek == 39) { // $' - suffix
274 ++position;
275 builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length);
276 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
277 ++position;
278 var n = peek - 48;
279 if (position < length) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100280 peek = %_StringCharCodeAt(string, position);
Steve Blocka7e24c12009-10-30 11:49:00 +0000281 // $nn, 01 <= nn <= 99
282 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
283 var nn = n * 10 + (peek - 48);
284 if (nn < m) {
285 // If the two digit capture reference is within range of
286 // the captures, we use it instead of the single digit
287 // one. Otherwise, we fall back to using the single
288 // digit reference. This matches the behavior of
289 // SpiderMonkey.
290 ++position;
291 n = nn;
292 }
293 }
294 }
295 if (0 < n && n < m) {
296 addCaptureString(builder, matchInfo, n);
297 } else {
298 // Because of the captures range check in the parsing of two
299 // digit capture references, we can only enter here when a
300 // single digit capture reference is outside the range of
301 // captures.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100302 builder_elements.push('$');
Steve Blocka7e24c12009-10-30 11:49:00 +0000303 --position;
304 }
305 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100306 builder_elements.push('$');
Steve Blocka7e24c12009-10-30 11:49:00 +0000307 }
308 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100309 builder_elements.push('$');
Steve Blocka7e24c12009-10-30 11:49:00 +0000310 }
311
312 // Go the the next $ in the string.
313 next = %StringIndexOf(string, '$', position);
314
315 // Return if there are no more $ characters in the string. If we
316 // haven't reached the end, we need to append the suffix.
317 if (next < 0) {
318 if (position < length) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100319 builder_elements.push(SubString(string, position, length));
Steve Blocka7e24c12009-10-30 11:49:00 +0000320 }
321 return;
322 }
323
324 // Append substring between the previous and the next $ character.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100325 if (next > position) {
326 builder_elements.push(SubString(string, position, next));
327 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000328 }
329};
330
331
332// Compute the string of a given regular expression capture.
333function CaptureString(string, lastCaptureInfo, index) {
334 // Scale the index.
335 var scaled = index << 1;
336 // Compute start and end.
337 var start = lastCaptureInfo[CAPTURE(scaled)];
Kristian Monsen25f61362010-05-21 11:50:48 +0100338 // If start isn't valid, return undefined.
339 if (start < 0) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000340 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
Steve Blocka7e24c12009-10-30 11:49:00 +0000341 return SubString(string, start, end);
342};
343
344
345// Add the string of a given regular expression capture to the
346// ReplaceResultBuilder
347function addCaptureString(builder, matchInfo, index) {
348 // Scale the index.
349 var scaled = index << 1;
350 // Compute start and end.
351 var start = matchInfo[CAPTURE(scaled)];
Kristian Monsen25f61362010-05-21 11:50:48 +0100352 if (start < 0) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000353 var end = matchInfo[CAPTURE(scaled + 1)];
Steve Blocka7e24c12009-10-30 11:49:00 +0000354 builder.addSpecialSlice(start, end);
355};
356
Steve Block6ded16b2010-05-10 14:33:55 +0100357// TODO(lrn): This array will survive indefinitely if replace is never
358// called again. However, it will be empty, since the contents are cleared
359// in the finally block.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100360var reusableReplaceArray = new InternalArray(16);
Steve Blocka7e24c12009-10-30 11:49:00 +0000361
362// Helper function for replacing regular expressions with the result of a
Steve Block6ded16b2010-05-10 14:33:55 +0100363// function application in String.prototype.replace.
Kristian Monsen25f61362010-05-21 11:50:48 +0100364function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
365 var resultArray = reusableReplaceArray;
366 if (resultArray) {
367 reusableReplaceArray = null;
368 } else {
369 // Inside a nested replace (replace called from the replacement function
370 // of another replace) or we have failed to set the reusable array
371 // back due to an exception in a replacement function. Create a new
372 // array to use in the future, or until the original is written back.
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100373 resultArray = new InternalArray(16);
Steve Blocka7e24c12009-10-30 11:49:00 +0000374 }
Kristian Monsen25f61362010-05-21 11:50:48 +0100375 var res = %RegExpExecMultiple(regexp,
376 subject,
377 lastMatchInfo,
378 resultArray);
379 regexp.lastIndex = 0;
380 if (IS_NULL(res)) {
381 // No matches at all.
382 reusableReplaceArray = resultArray;
383 return subject;
384 }
385 var len = res.length;
386 var i = 0;
387 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
388 var match_start = 0;
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100389 var override = new InternalArray(null, 0, subject);
Kristian Monsen25f61362010-05-21 11:50:48 +0100390 var receiver = %GetGlobalReceiver();
391 while (i < len) {
392 var elem = res[i];
393 if (%_IsSmi(elem)) {
394 if (elem > 0) {
395 match_start = (elem >> 11) + (elem & 0x7ff);
396 } else {
397 match_start = res[++i] - elem;
398 }
399 } else {
400 override[0] = elem;
401 override[1] = match_start;
402 lastMatchInfoOverride = override;
403 var func_result =
404 %_CallFunction(receiver, elem, match_start, subject, replace);
Steve Block1e0659c2011-05-24 12:43:12 +0100405 res[i] = TO_STRING_INLINE(func_result);
Kristian Monsen25f61362010-05-21 11:50:48 +0100406 match_start += elem.length;
407 }
408 i++;
409 }
410 } else {
411 while (i < len) {
412 var elem = res[i];
413 if (!%_IsSmi(elem)) {
414 // elem must be an Array.
415 // Use the apply argument as backing for global RegExp properties.
416 lastMatchInfoOverride = elem;
417 var func_result = replace.apply(null, elem);
Steve Block1e0659c2011-05-24 12:43:12 +0100418 res[i] = TO_STRING_INLINE(func_result);
Kristian Monsen25f61362010-05-21 11:50:48 +0100419 }
420 i++;
421 }
422 }
423 var resultBuilder = new ReplaceResultBuilder(subject, res);
424 var result = resultBuilder.generate();
425 resultArray.length = 0;
426 reusableReplaceArray = resultArray;
427 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000428}
429
430
Kristian Monsen25f61362010-05-21 11:50:48 +0100431function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
432 var matchInfo = DoRegExpExec(regexp, subject, 0);
433 if (IS_NULL(matchInfo)) return subject;
434 var result = new ReplaceResultBuilder(subject);
435 var index = matchInfo[CAPTURE0];
436 result.addSpecialSlice(0, index);
437 var endOfMatch = matchInfo[CAPTURE1];
Steve Blocka7e24c12009-10-30 11:49:00 +0000438 // Compute the parameter list consisting of the match, captures, index,
439 // and subject for the replace function invocation.
Steve Blocka7e24c12009-10-30 11:49:00 +0000440 // The number of captures plus one for the match.
441 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
Kristian Monsen25f61362010-05-21 11:50:48 +0100442 var replacement;
Steve Blocka7e24c12009-10-30 11:49:00 +0000443 if (m == 1) {
Kristian Monsen25f61362010-05-21 11:50:48 +0100444 // No captures, only the match, which is always valid.
445 var s = SubString(subject, index, endOfMatch);
Steve Blocka7e24c12009-10-30 11:49:00 +0000446 // Don't call directly to avoid exposing the built-in global object.
Kristian Monsen25f61362010-05-21 11:50:48 +0100447 replacement =
448 %_CallFunction(%GetGlobalReceiver(), s, index, subject, replace);
449 } else {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100450 var parameters = new InternalArray(m + 2);
Kristian Monsen25f61362010-05-21 11:50:48 +0100451 for (var j = 0; j < m; j++) {
452 parameters[j] = CaptureString(subject, matchInfo, j);
453 }
454 parameters[j] = index;
455 parameters[j + 1] = subject;
456
457 replacement = replace.apply(null, parameters);
Steve Blocka7e24c12009-10-30 11:49:00 +0000458 }
Kristian Monsen25f61362010-05-21 11:50:48 +0100459
460 result.add(replacement); // The add method converts to string if necessary.
461 // Can't use matchInfo any more from here, since the function could
462 // overwrite it.
463 result.addSpecialSlice(endOfMatch, subject.length);
464 return result.generate();
Steve Blocka7e24c12009-10-30 11:49:00 +0000465}
466
Kristian Monsen25f61362010-05-21 11:50:48 +0100467
Steve Blocka7e24c12009-10-30 11:49:00 +0000468// ECMA-262 section 15.5.4.12
469function StringSearch(re) {
Steve Block6ded16b2010-05-10 14:33:55 +0100470 var regexp;
471 if (IS_STRING(re)) {
472 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
473 } else if (IS_REGEXP(re)) {
474 regexp = re;
475 } else {
476 regexp = new $RegExp(re);
477 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100478 var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100479 if (match) {
480 return match[CAPTURE0];
481 }
482 return -1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000483}
484
485
486// ECMA-262 section 15.5.4.13
487function StringSlice(start, end) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000488 var s = TO_STRING_INLINE(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000489 var s_len = s.length;
490 var start_i = TO_INTEGER(start);
491 var end_i = s_len;
492 if (end !== void 0)
493 end_i = TO_INTEGER(end);
494
495 if (start_i < 0) {
496 start_i += s_len;
497 if (start_i < 0)
498 start_i = 0;
499 } else {
500 if (start_i > s_len)
501 start_i = s_len;
502 }
503
504 if (end_i < 0) {
505 end_i += s_len;
506 if (end_i < 0)
507 end_i = 0;
508 } else {
509 if (end_i > s_len)
510 end_i = s_len;
511 }
512
513 var num_c = end_i - start_i;
514 if (num_c < 0)
515 num_c = 0;
516
517 return SubString(s, start_i, start_i + num_c);
518}
519
520
521// ECMA-262 section 15.5.4.14
522function StringSplit(separator, limit) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000523 var subject = TO_STRING_INLINE(this);
Leon Clarkee46be812010-01-19 14:06:41 +0000524 limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
Steve Blocka7e24c12009-10-30 11:49:00 +0000525 if (limit === 0) return [];
526
527 // ECMA-262 says that if separator is undefined, the result should
528 // be an array of size 1 containing the entire string. SpiderMonkey
Steve Block6ded16b2010-05-10 14:33:55 +0100529 // and KJS have this behavior only when no separator is given. If
Steve Blocka7e24c12009-10-30 11:49:00 +0000530 // undefined is explicitly given, they convert it to a string and
531 // use that. We do as SpiderMonkey and KJS.
532 if (%_ArgumentsLength() === 0) {
533 return [subject];
534 }
535
536 var length = subject.length;
Andrei Popescu402d9372010-02-26 13:31:12 +0000537 if (!IS_REGEXP(separator)) {
538 separator = TO_STRING_INLINE(separator);
539 var separator_length = separator.length;
540
Steve Blocka7e24c12009-10-30 11:49:00 +0000541 // If the separator string is empty then return the elements in the subject.
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800542 if (separator_length === 0) return %StringToArray(subject, limit);
Andrei Popescu402d9372010-02-26 13:31:12 +0000543
Steve Block6ded16b2010-05-10 14:33:55 +0100544 var result = %StringSplit(subject, separator, limit);
Andrei Popescu402d9372010-02-26 13:31:12 +0000545
546 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000547 }
548
Andrei Popescu402d9372010-02-26 13:31:12 +0000549 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
550
Steve Blocka7e24c12009-10-30 11:49:00 +0000551 if (length === 0) {
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800552 if (DoRegExpExec(separator, subject, 0, 0) != null) {
Steve Block6ded16b2010-05-10 14:33:55 +0100553 return [];
554 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000555 return [subject];
556 }
557
558 var currentIndex = 0;
559 var startIndex = 0;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100560 var startMatch = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +0000561 var result = [];
562
Steve Block6ded16b2010-05-10 14:33:55 +0100563 outer_loop:
Steve Blocka7e24c12009-10-30 11:49:00 +0000564 while (true) {
565
566 if (startIndex === length) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100567 result.push(SubString(subject, currentIndex, length));
Steve Block6ded16b2010-05-10 14:33:55 +0100568 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000569 }
570
Ben Murdochb0fe1622011-05-05 13:52:32 +0100571 var matchInfo = DoRegExpExec(separator, subject, startIndex);
572 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
573 result.push(SubString(subject, currentIndex, length));
Steve Block6ded16b2010-05-10 14:33:55 +0100574 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000575 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000576 var endIndex = matchInfo[CAPTURE1];
577
578 // We ignore a zero-length match at the currentIndex.
579 if (startIndex === endIndex && endIndex === currentIndex) {
580 startIndex++;
581 continue;
582 }
583
Ben Murdochb0fe1622011-05-05 13:52:32 +0100584 if (currentIndex + 1 == startMatch) {
585 result.push(%_StringCharAt(subject, currentIndex));
586 } else {
587 result.push(%_SubString(subject, currentIndex, startMatch));
588 }
589
Steve Block6ded16b2010-05-10 14:33:55 +0100590 if (result.length === limit) break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000591
Ben Murdochb0fe1622011-05-05 13:52:32 +0100592 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
593 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
594 var start = matchInfo[i++];
595 var end = matchInfo[i++];
596 if (end != -1) {
597 if (start + 1 == end) {
598 result.push(%_StringCharAt(subject, start));
599 } else {
600 result.push(%_SubString(subject, start, end));
601 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000602 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100603 result.push(void 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000604 }
Steve Block6ded16b2010-05-10 14:33:55 +0100605 if (result.length === limit) break outer_loop;
Steve Blocka7e24c12009-10-30 11:49:00 +0000606 }
607
608 startIndex = currentIndex = endIndex;
609 }
Steve Block6ded16b2010-05-10 14:33:55 +0100610 return result;
Steve Blocka7e24c12009-10-30 11:49:00 +0000611}
612
613
Steve Blocka7e24c12009-10-30 11:49:00 +0000614// ECMA-262 section 15.5.4.15
615function StringSubstring(start, end) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000616 var s = TO_STRING_INLINE(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000617 var s_len = s.length;
Leon Clarkee46be812010-01-19 14:06:41 +0000618
Steve Blocka7e24c12009-10-30 11:49:00 +0000619 var start_i = TO_INTEGER(start);
Leon Clarkee46be812010-01-19 14:06:41 +0000620 if (start_i < 0) {
621 start_i = 0;
622 } else if (start_i > s_len) {
623 start_i = s_len;
624 }
625
Steve Blocka7e24c12009-10-30 11:49:00 +0000626 var end_i = s_len;
Leon Clarkee46be812010-01-19 14:06:41 +0000627 if (!IS_UNDEFINED(end)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000628 end_i = TO_INTEGER(end);
Leon Clarkee46be812010-01-19 14:06:41 +0000629 if (end_i > s_len) {
630 end_i = s_len;
631 } else {
632 if (end_i < 0) end_i = 0;
633 if (start_i > end_i) {
634 var tmp = end_i;
635 end_i = start_i;
636 start_i = tmp;
637 }
638 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000639 }
640
Ben Murdochb0fe1622011-05-05 13:52:32 +0100641 return (start_i + 1 == end_i
642 ? %_StringCharAt(s, start_i)
643 : %_SubString(s, start_i, end_i));
Steve Blocka7e24c12009-10-30 11:49:00 +0000644}
645
646
647// This is not a part of ECMA-262.
648function StringSubstr(start, n) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000649 var s = TO_STRING_INLINE(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000650 var len;
651
652 // Correct n: If not given, set to string length; if explicitly
653 // set to undefined, zero, or negative, returns empty string.
654 if (n === void 0) {
655 len = s.length;
656 } else {
657 len = TO_INTEGER(n);
658 if (len <= 0) return '';
659 }
660
661 // Correct start: If not given (or undefined), set to zero; otherwise
662 // convert to integer and handle negative case.
663 if (start === void 0) {
664 start = 0;
665 } else {
666 start = TO_INTEGER(start);
667 // If positive, and greater than or equal to the string length,
668 // return empty string.
669 if (start >= s.length) return '';
670 // If negative and absolute value is larger than the string length,
671 // use zero.
672 if (start < 0) {
673 start += s.length;
674 if (start < 0) start = 0;
675 }
676 }
677
678 var end = start + len;
679 if (end > s.length) end = s.length;
680
Ben Murdochb0fe1622011-05-05 13:52:32 +0100681 return (start + 1 == end
682 ? %_StringCharAt(s, start)
683 : %_SubString(s, start, end));
Steve Blocka7e24c12009-10-30 11:49:00 +0000684}
685
686
687// ECMA-262, 15.5.4.16
688function StringToLowerCase() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000689 return %StringToLowerCase(TO_STRING_INLINE(this));
Steve Blocka7e24c12009-10-30 11:49:00 +0000690}
691
692
693// ECMA-262, 15.5.4.17
694function StringToLocaleLowerCase() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000695 return %StringToLowerCase(TO_STRING_INLINE(this));
Steve Blocka7e24c12009-10-30 11:49:00 +0000696}
697
698
699// ECMA-262, 15.5.4.18
700function StringToUpperCase() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000701 return %StringToUpperCase(TO_STRING_INLINE(this));
Steve Blocka7e24c12009-10-30 11:49:00 +0000702}
703
704
705// ECMA-262, 15.5.4.19
706function StringToLocaleUpperCase() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000707 return %StringToUpperCase(TO_STRING_INLINE(this));
Steve Blocka7e24c12009-10-30 11:49:00 +0000708}
709
Steve Block3ce2e202009-11-05 08:53:23 +0000710// ES5, 15.5.4.20
711function StringTrim() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000712 return %StringTrim(TO_STRING_INLINE(this), true, true);
Steve Block3ce2e202009-11-05 08:53:23 +0000713}
714
715function StringTrimLeft() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000716 return %StringTrim(TO_STRING_INLINE(this), true, false);
Steve Block3ce2e202009-11-05 08:53:23 +0000717}
718
719function StringTrimRight() {
Andrei Popescu402d9372010-02-26 13:31:12 +0000720 return %StringTrim(TO_STRING_INLINE(this), false, true);
Steve Block3ce2e202009-11-05 08:53:23 +0000721}
Steve Blocka7e24c12009-10-30 11:49:00 +0000722
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100723var static_charcode_array = new InternalArray(4);
Steve Block6ded16b2010-05-10 14:33:55 +0100724
Steve Blocka7e24c12009-10-30 11:49:00 +0000725// ECMA-262, section 15.5.3.2
726function StringFromCharCode(code) {
727 var n = %_ArgumentsLength();
Steve Block6ded16b2010-05-10 14:33:55 +0100728 if (n == 1) {
729 if (!%_IsSmi(code)) code = ToNumber(code);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100730 return %_StringCharFromCode(code & 0xffff);
Steve Block6ded16b2010-05-10 14:33:55 +0100731 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000732
733 // NOTE: This is not super-efficient, but it is necessary because we
734 // want to avoid converting to numbers from within the virtual
735 // machine. Maybe we can find another way of doing this?
Steve Block6ded16b2010-05-10 14:33:55 +0100736 var codes = static_charcode_array;
737 for (var i = 0; i < n; i++) {
738 var code = %_Arguments(i);
739 if (!%_IsSmi(code)) code = ToNumber(code);
740 codes[i] = code;
741 }
742 codes.length = n;
Steve Blocka7e24c12009-10-30 11:49:00 +0000743 return %StringFromCharCodeArray(codes);
744}
745
746
747// Helper function for very basic XSS protection.
748function HtmlEscape(str) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000749 return TO_STRING_INLINE(str).replace(/</g, "&lt;")
750 .replace(/>/g, "&gt;")
751 .replace(/"/g, "&quot;")
752 .replace(/'/g, "&#039;");
Steve Blocka7e24c12009-10-30 11:49:00 +0000753};
754
755
756// Compatibility support for KJS.
757// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
758function StringLink(s) {
759 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
760}
761
762
763function StringAnchor(name) {
764 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
765}
766
767
768function StringFontcolor(color) {
769 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
770}
771
772
773function StringFontsize(size) {
774 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
775}
776
777
778function StringBig() {
779 return "<big>" + this + "</big>";
780}
781
782
783function StringBlink() {
784 return "<blink>" + this + "</blink>";
785}
786
787
788function StringBold() {
789 return "<b>" + this + "</b>";
790}
791
792
793function StringFixed() {
794 return "<tt>" + this + "</tt>";
795}
796
797
798function StringItalics() {
799 return "<i>" + this + "</i>";
800}
801
802
803function StringSmall() {
804 return "<small>" + this + "</small>";
805}
806
807
808function StringStrike() {
809 return "<strike>" + this + "</strike>";
810}
811
812
813function StringSub() {
814 return "<sub>" + this + "</sub>";
815}
816
817
818function StringSup() {
819 return "<sup>" + this + "</sup>";
820}
821
822
Leon Clarkee46be812010-01-19 14:06:41 +0000823// ReplaceResultBuilder support.
Steve Blocka7e24c12009-10-30 11:49:00 +0000824function ReplaceResultBuilder(str) {
Steve Block6ded16b2010-05-10 14:33:55 +0100825 if (%_ArgumentsLength() > 1) {
826 this.elements = %_Arguments(1);
827 } else {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100828 this.elements = new InternalArray();
Steve Block6ded16b2010-05-10 14:33:55 +0100829 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000830 this.special_string = str;
831}
832
833
Leon Clarkee46be812010-01-19 14:06:41 +0000834ReplaceResultBuilder.prototype.add = function(str) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000835 str = TO_STRING_INLINE(str);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100836 if (str.length > 0) this.elements.push(str);
Steve Blocka7e24c12009-10-30 11:49:00 +0000837}
838
839
840ReplaceResultBuilder.prototype.addSpecialSlice = function(start, end) {
841 var len = end - start;
Steve Block6ded16b2010-05-10 14:33:55 +0100842 if (start < 0 || len <= 0) return;
Steve Blockd0582a62009-12-15 09:54:21 +0000843 if (start < 0x80000 && len < 0x800) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100844 this.elements.push((start << 11) | len);
Steve Blocka7e24c12009-10-30 11:49:00 +0000845 } else {
Steve Blockd0582a62009-12-15 09:54:21 +0000846 // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength,
847 // so -len is a smi.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100848 var elements = this.elements;
849 elements.push(-len);
850 elements.push(start);
Steve Blocka7e24c12009-10-30 11:49:00 +0000851 }
852}
853
854
Steve Blocka7e24c12009-10-30 11:49:00 +0000855ReplaceResultBuilder.prototype.generate = function() {
Leon Clarkee46be812010-01-19 14:06:41 +0000856 var elements = this.elements;
857 return %StringBuilderConcat(elements, elements.length, this.special_string);
Steve Blocka7e24c12009-10-30 11:49:00 +0000858}
859
860
Steve Blocka7e24c12009-10-30 11:49:00 +0000861// -------------------------------------------------------------------
862
863function SetupString() {
864 // Setup the constructor property on the String prototype object.
865 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
866
867
868 // Setup the non-enumerable functions on the String object.
869 InstallFunctions($String, DONT_ENUM, $Array(
870 "fromCharCode", StringFromCharCode
871 ));
872
873
874 // Setup the non-enumerable functions on the String prototype object.
875 InstallFunctionsOnHiddenPrototype($String.prototype, DONT_ENUM, $Array(
876 "valueOf", StringValueOf,
877 "toString", StringToString,
878 "charAt", StringCharAt,
879 "charCodeAt", StringCharCodeAt,
880 "concat", StringConcat,
881 "indexOf", StringIndexOf,
882 "lastIndexOf", StringLastIndexOf,
883 "localeCompare", StringLocaleCompare,
884 "match", StringMatch,
885 "replace", StringReplace,
886 "search", StringSearch,
887 "slice", StringSlice,
888 "split", StringSplit,
889 "substring", StringSubstring,
890 "substr", StringSubstr,
891 "toLowerCase", StringToLowerCase,
892 "toLocaleLowerCase", StringToLocaleLowerCase,
893 "toUpperCase", StringToUpperCase,
894 "toLocaleUpperCase", StringToLocaleUpperCase,
Steve Block3ce2e202009-11-05 08:53:23 +0000895 "trim", StringTrim,
896 "trimLeft", StringTrimLeft,
897 "trimRight", StringTrimRight,
Steve Blocka7e24c12009-10-30 11:49:00 +0000898 "link", StringLink,
899 "anchor", StringAnchor,
900 "fontcolor", StringFontcolor,
901 "fontsize", StringFontsize,
902 "big", StringBig,
903 "blink", StringBlink,
904 "bold", StringBold,
905 "fixed", StringFixed,
906 "italics", StringItalics,
907 "small", StringSmall,
908 "strike", StringStrike,
909 "sub", StringSub,
Ben Murdochb0fe1622011-05-05 13:52:32 +0100910 "sup", StringSup
Steve Blocka7e24c12009-10-30 11:49:00 +0000911 ));
912}
913
914
915SetupString();