blob: 984d969cc93363d662a49a174310e5c137a5510f [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 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) {
37 var value = %_ArgumentsLength() == 0 ? '' : ToString(x);
mads.s.ager31e71382008-08-13 09:32:07 +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.org5a8ca6c2008-10-23 13:57:19 +000049 if (!IS_STRING(this) && !%HasStringClass(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.org5a8ca6c2008-10-23 13:57:19 +000057 if (!IS_STRING(this) && !%HasStringClass(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) {
65 var subject = ToString(this);
66 var index = TO_INTEGER(pos);
67 if (index >= subject.length || index < 0) return "";
68 return %CharFromCode(%StringCharCodeAt(subject, index));
69}
70
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000071
72// ECMA-262 section 15.5.4.5
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000073function StringCharCodeAt(pos) {
kasper.lund7276f142008-07-30 08:49:36 +000074 var fast_answer = %_FastCharCodeAt(this, pos);
75 if (%_IsSmi(fast_answer)) {
76 return fast_answer;
77 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000078 var subject = ToString(this);
79 var index = TO_INTEGER(pos);
80 return %StringCharCodeAt(subject, index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000081}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082
83
84// ECMA-262, section 15.5.4.6
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000085function StringConcat() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000086 var len = %_ArgumentsLength();
87 var parts = new $Array(len + 1);
88 parts[0] = ToString(this);
89 for (var i = 0; i < len; i++)
90 parts[i + 1] = ToString(%_Arguments(i));
91 return parts.join('');
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000092}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000093
94// Match ES3 and Safari
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000095%FunctionSetLength(StringConcat, 1);
96
97
98// ECMA-262 section 15.5.4.7
99function StringIndexOf(searchString /* position */) { // length == 1
100 var subject_str = ToString(this);
101 var pattern_str = ToString(searchString);
102 var subject_str_len = subject_str.length;
103 var pattern_str_len = pattern_str.length;
104 var index = 0;
105 if (%_ArgumentsLength() > 1) {
106 var arg1 = %_Arguments(1); // position
107 index = TO_INTEGER(arg1);
108 }
109 if (index < 0) index = 0;
110 if (index > subject_str_len) index = subject_str_len;
111 if (pattern_str_len + index > subject_str_len) return -1;
112 return %StringIndexOf(subject_str, pattern_str, index);
113}
114
115
116// ECMA-262 section 15.5.4.8
117function StringLastIndexOf(searchString /* position */) { // length == 1
118 var sub = ToString(this);
119 var pat = ToString(searchString);
120 var index = (%_ArgumentsLength() > 1)
121 ? ToNumber(%_Arguments(1) /* position */)
122 : $NaN;
123 var firstIndex;
124 if ($isNaN(index)) {
125 firstIndex = sub.length - pat.length;
126 } else {
127 firstIndex = TO_INTEGER(index);
128 if (firstIndex + pat.length > sub.length) {
129 firstIndex = sub.length - pat.length;
130 }
131 }
132 return %StringLastIndexOf(sub, pat, firstIndex);
133}
134
135
136// ECMA-262 section 15.5.4.9
137//
138// This function is implementation specific. For now, we do not
139// do anything locale specific.
140function StringLocaleCompare(other) {
141 if (%_ArgumentsLength() === 0) return 0;
142
143 var this_str = ToString(this);
144 var other_str = ToString(other);
145 return %StringLocaleCompare(this_str, other_str);
146}
147
148
149// ECMA-262 section 15.5.4.10
150function StringMatch(regexp) {
151 if (!IS_REGEXP(regexp)) regexp = new ORIGINAL_REGEXP(regexp);
152 var subject = ToString(this);
153
154 if (!regexp.global) return regexp.exec(subject);
155 var matches = DoRegExpExecGlobal(regexp, subject);
156
157 // If the regexp did not match, return null.
158 if (matches.length == 0) return null;
159
160 // Build the result array.
161 var result = new $Array(match_string);
162 for (var i = 0; i < matches.length; ++i) {
163 var match = matches[i];
164 var match_string = subject.slice(match[0], match[1]);
165 result[i] = match_string;
166 }
167
168 return result;
169}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000170
171
172// SubString is an internal function that returns the sub string of 'string'.
173// If resulting string is of length 1, we use the one character cache
174// otherwise we call the runtime system.
175function SubString(string, start, end) {
176 // Use the one character string cache.
177 if (start + 1 == end) return %CharFromCode(%StringCharCodeAt(string, start));
178 return %StringSlice(string, start, end);
179}
180
181
182// ECMA-262, section 15.5.4.11
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000183function StringReplace(search, replace) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000184 var subject = ToString(this);
185
186 // Delegate to one of the regular expression variants if necessary.
187 if (IS_REGEXP(search)) {
188 if (IS_FUNCTION(replace)) {
189 return StringReplaceRegExpWithFunction(subject, search, replace);
190 } else {
191 return StringReplaceRegExp(subject, search, replace);
192 }
193 }
194
195 // Convert the search argument to a string and search for it.
196 search = ToString(search);
197 var start = %StringIndexOf(subject, search, 0);
198 if (start < 0) return subject;
199 var end = start + search.length;
200
201 var builder = new StringBuilder();
202 // prefix
203 builder.add(SubString(subject, 0, start));
204
205 // Compute the string to replace with.
206 if (IS_FUNCTION(replace)) {
207 builder.add(replace.call(null, search, start, subject));
208 } else {
209 ExpandReplacement(ToString(replace), subject, [ start, end ], builder);
210 }
211
212 // suffix
213 builder.add(SubString(subject, end, subject.length));
214
215 return builder.generate();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000216}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000217
218
219// Helper function for regular expressions in String.prototype.replace.
220function StringReplaceRegExp(subject, regexp, replace) {
221 // Compute an array of matches; each match is really a list of
222 // captures - pairs of (start, end) indexes into the subject string.
223 var matches;
224 if (regexp.global) {
225 matches = DoRegExpExecGlobal(regexp, subject);
226 if (matches.length == 0) return subject;
227 } else {
228 var captures = DoRegExpExec(regexp, subject, 0);
229 if (IS_NULL(captures)) return subject;
230 matches = [ captures ];
231 }
232
233 // Determine the number of matches.
234 var length = matches.length;
235
236 // Build the resulting string of subject slices and replacements.
237 var result = new StringBuilder();
238 var previous = 0;
239 // The caller of StringReplaceRegExp must ensure that replace is not a
240 // function.
241 replace = ToString(replace);
242 for (var i = 0; i < length; i++) {
243 var captures = matches[i];
244 result.add(SubString(subject, previous, captures[0]));
245 ExpandReplacement(replace, subject, captures, result);
246 previous = captures[1]; // continue after match
247 }
248 result.add(SubString(subject, previous, subject.length));
249 return result.generate();
250};
251
252
253// Expand the $-expressions in the string and return a new string with
254// the result.
255function ExpandReplacement(string, subject, captures, builder) {
256 var next = %StringIndexOf(string, '$', 0);
257 if (next < 0) {
258 builder.add(string);
259 return;
260 }
261
262 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
263 var m = captures.length >> 1; // includes the match
264
265 if (next > 0) builder.add(SubString(string, 0, next));
266 var length = string.length;
267
268 while (true) {
269 var expansion = '$';
270 var position = next + 1;
271 if (position < length) {
272 var peek = %StringCharCodeAt(string, position);
273 if (peek == 36) { // $$
274 ++position;
275 } else if (peek == 38) { // $& - match
276 ++position;
277 expansion = SubString(subject, captures[0], captures[1]);
278 } else if (peek == 96) { // $` - prefix
279 ++position;
280 expansion = SubString(subject, 0, captures[0]);
281 } else if (peek == 39) { // $' - suffix
282 ++position;
283 expansion = SubString(subject, captures[1], subject.length);
284 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
285 ++position;
286 var n = peek - 48;
287 if (position < length) {
288 peek = %StringCharCodeAt(string, position);
289 // $nn, 01 <= nn <= 99
290 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
291 var nn = n * 10 + (peek - 48);
292 if (nn < m) {
293 // If the two digit capture reference is within range of
294 // the captures, we use it instead of the single digit
295 // one. Otherwise, we fall back to using the single
296 // digit reference. This matches the behavior of
297 // SpiderMonkey.
298 ++position;
299 n = nn;
300 }
301 }
302 }
303 if (0 < n && n < m) {
304 expansion = CaptureString(subject, captures, n);
305 if (IS_UNDEFINED(expansion)) expansion = "";
306 } else {
307 // Because of the captures range check in the parsing of two
308 // digit capture references, we can only enter here when a
309 // single digit capture reference is outside the range of
310 // captures.
311 --position;
312 }
313 }
314 }
315
316 // Append the $ expansion and go the the next $ in the string.
317 builder.add(expansion);
318 next = %StringIndexOf(string, '$', position);
319
320 // Return if there are no more $ characters in the string. If we
321 // haven't reached the end, we need to append the suffix.
322 if (next < 0) {
323 if (position < length) {
324 builder.add(SubString(string, position, length));
325 }
326 return;
327 }
328
329 // Append substring between the previous and the next $ character.
330 builder.add(SubString(string, position, next));
331 }
332};
333
334
335// Compute the string of a given PCRE capture.
336function CaptureString(string, captures, index) {
337 // Scale the index.
338 var scaled = index << 1;
339 // Compute start and end.
340 var start = captures[scaled];
341 var end = captures[scaled + 1];
342 // If either start or end is missing return undefined.
343 if (start < 0 || end < 0) return;
344 return SubString(string, start, end);
345};
346
347
348// Helper function for replacing regular expressions with the result of a
349// function application in String.prototype.replace. The function application
350// must be interleaved with the regexp matching (contrary to ECMA-262
351// 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses
352// the static properties of the RegExp constructor. Example:
353// 'abcd'.replace(/(.)/g, function() { return RegExp.$1; }
354// should be 'abcd' and not 'dddd' (or anything else).
355function StringReplaceRegExpWithFunction(subject, regexp, replace) {
356 var result = new ReplaceResultBuilder(subject);
357 // Captures is an array of pairs of (start, end) indices for the match and
358 // any captured substrings.
359 var captures = DoRegExpExec(regexp, subject, 0);
360 if (IS_NULL(captures)) return subject;
361
362 // There's at least one match. If the regexp is global, we have to loop
363 // over all matches. The loop is not in C++ code here like the one in
364 // RegExp.prototype.exec, because of the interleaved function application.
365 // Unfortunately, that means this code is nearly duplicated, here and in
366 // jsregexp.cc.
367 if (regexp.global) {
368 var previous = 0;
369 do {
370 result.addSpecialSlice(previous, captures[0]);
371 result.add(ApplyReplacementFunction(replace, captures, subject));
372 // Continue with the next match.
373 previous = captures[1];
374 // Increment previous if we matched an empty string, as per ECMA-262
375 // 15.5.4.10.
376 if (captures[0] == captures[1]) previous++;
377
378 // Per ECMA-262 15.10.6.2, if the previous index is greater than the
379 // string length, there is no match
380 captures = (previous > subject.length)
381 ? null
382 : DoRegExpExec(regexp, subject, previous);
383 } while (!IS_NULL(captures));
384
385 // Tack on the final right substring after the last match, if necessary.
386 if (previous < subject.length) {
387 result.addSpecialSlice(previous, subject.length);
388 }
389 } else { // Not a global regexp, no need to loop.
390 result.addSpecialSlice(0, captures[0]);
391 result.add(ApplyReplacementFunction(replace, captures, subject));
392 result.addSpecialSlice(captures[1], subject.length);
393 }
394
395 return result.generate();
396}
397
398
399// Helper function to apply a string replacement function once.
400function ApplyReplacementFunction(replace, captures, subject) {
401 // Compute the parameter list consisting of the match, captures, index,
402 // and subject for the replace function invocation.
403 var index = captures[0];
404 // The number of captures plus one for the match.
405 var m = captures.length >> 1;
406 if (m == 1) {
407 var s = CaptureString(subject, captures, 0);
408 return ToString(replace.call(null, s, index, subject));
409 }
410 var parameters = $Array(m + 2);
411 for (var j = 0; j < m; j++) {
412 parameters[j] = CaptureString(subject, captures, j);
413 }
414 parameters[j] = index;
415 parameters[j + 1] = subject;
416 return ToString(replace.apply(null, parameters));
417}
418
419
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000420// ECMA-262 section 15.5.4.12
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000421function StringSearch(re) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000422 var regexp = new ORIGINAL_REGEXP(re);
423 var s = ToString(this);
424 var last_idx = regexp.lastIndex; // keep old lastIndex
425 regexp.lastIndex = 0; // ignore re.global property
426 var result = regexp.exec(s);
427 regexp.lastIndex = last_idx; // restore lastIndex
428 if (result == null)
429 return -1;
430 else
431 return result.index;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000432}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000433
434
435// ECMA-262 section 15.5.4.13
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000436function StringSlice(start, end) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000437 var s = ToString(this);
438 var s_len = s.length;
439 var start_i = TO_INTEGER(start);
440 var end_i = s_len;
441 if (end !== void 0)
442 end_i = TO_INTEGER(end);
443
444 if (start_i < 0) {
445 start_i += s_len;
446 if (start_i < 0)
447 start_i = 0;
448 } else {
449 if (start_i > s_len)
450 start_i = s_len;
451 }
452
453 if (end_i < 0) {
454 end_i += s_len;
455 if (end_i < 0)
456 end_i = 0;
457 } else {
458 if (end_i > s_len)
459 end_i = s_len;
460 }
461
462 var num_c = end_i - start_i;
463 if (num_c < 0)
464 num_c = 0;
465
466 return SubString(s, start_i, start_i + num_c);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000467}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000468
469
470// ECMA-262 section 15.5.4.14
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000471function StringSplit(separator, limit) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000472 var subject = ToString(this);
473 var result = [];
474 var lim = (limit === void 0) ? 0xffffffff : ToUint32(limit);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000475
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000476 if (lim === 0) return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000477
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000478 // ECMA-262 says that if separator is undefined, the result should
479 // be an array of size 1 containing the entire string. SpiderMonkey
480 // and KJS have this behaviour only when no separator is given. If
481 // undefined is explicitly given, they convert it to a string and
482 // use that. We do as SpiderMonkey and KJS.
483 if (%_ArgumentsLength() === 0) {
484 result[result.length] = subject;
485 return result;
486 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488 var length = subject.length;
489 var currentIndex = 0;
490 var startIndex = 0;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000492 var sep = IS_REGEXP(separator) ? separator : ToString(separator);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000494 if (length === 0) {
495 if (splitMatch(sep, subject, 0, 0) != null) return result;
496 result[result.length] = subject;
497 return result;
498 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000499
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000500 while (true) {
501
502 if (startIndex === length) {
503 result[result.length] = subject.slice(currentIndex, length);
504 return result;
505 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000506
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000507 var match = splitMatch(sep, subject, currentIndex, startIndex);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000509 if (IS_NULL(match)) {
510 result[result.length] = subject.slice(currentIndex, length);
511 return result;
512 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000513
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000514 var endIndex = match[0];
515
516 // We ignore a zero-length match at the currentIndex.
517 if (startIndex === endIndex && endIndex === currentIndex) {
518 startIndex++;
519 continue;
520 }
521
522 result[result.length] = match[1];
523 if (result.length === lim) return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000524
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525 for (var i = 2; i < match.length; i++) {
526 result[result.length] = match[i];
527 if (result.length === lim) return result;
528 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000529
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000530 startIndex = currentIndex = endIndex;
531 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000532}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533
534
535// ECMA-262 section 15.5.4.14
536// Helper function used by split.
537function splitMatch(separator, subject, current_index, start_index) {
538 if (IS_REGEXP(separator)) {
539 var ovector = DoRegExpExec(separator, subject, start_index);
540 if (ovector == null) return null;
541 var nof_results = ovector.length >> 1;
542 var result = new $Array(nof_results + 1);
543 result[0] = ovector[1];
544 result[1] = subject.slice(current_index, ovector[0]);
545 for (var i = 1; i < nof_results; i++) {
546 var matching_start = ovector[2*i];
547 var matching_end = ovector[2*i + 1];
548 if (matching_start != -1 && matching_end != -1) {
549 result[i + 1] = subject.slice(matching_start, matching_end);
550 }
551 }
552 return result;
553 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555 var separatorIndex = subject.indexOf(separator, start_index);
556 if (separatorIndex === -1) return null;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 return [ separatorIndex + separator.length, subject.slice(current_index, separatorIndex) ];
559};
560
561
562// ECMA-262 section 15.5.4.15
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000563function StringSubstring(start, end) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000564 var s = ToString(this);
565 var s_len = s.length;
566 var start_i = TO_INTEGER(start);
567 var end_i = s_len;
568 if (!IS_UNDEFINED(end))
569 end_i = TO_INTEGER(end);
570
571 if (start_i < 0) start_i = 0;
572 if (start_i > s_len) start_i = s_len;
573 if (end_i < 0) end_i = 0;
574 if (end_i > s_len) end_i = s_len;
575
576 if (start_i > end_i) {
577 var tmp = end_i;
578 end_i = start_i;
579 start_i = tmp;
580 }
581
582 return SubString(s, start_i, end_i);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584
585
586// This is not a part of ECMA-262.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000587function StringSubstr(start, n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000588 var s = ToString(this);
589 var len;
590
591 // Correct n: If not given, set to string length; if explicitly
592 // set to undefined, zero, or negative, returns empty string.
593 if (n === void 0) {
594 len = s.length;
595 } else {
596 len = TO_INTEGER(n);
597 if (len <= 0) return '';
598 }
599
600 // Correct start: If not given (or undefined), set to zero; otherwise
601 // convert to integer and handle negative case.
602 if (start === void 0) {
603 start = 0;
604 } else {
605 start = TO_INTEGER(start);
606 // If positive, and greater than or equal to the string length,
607 // return empty string.
608 if (start >= s.length) return '';
609 // If negative and absolute value is larger than the string length,
610 // use zero.
611 if (start < 0) {
612 start += s.length;
613 if (start < 0) start = 0;
614 }
615 }
616
617 var end = start + len;
618 if (end > s.length) end = s.length;
619
620 return SubString(s, start, end);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000621}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000622
623
624// ECMA-262, 15.5.4.16
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000625function StringToLowerCase() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000626 return %StringToLowerCase(ToString(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000627}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000628
629
630// ECMA-262, 15.5.4.17
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000631function StringToLocaleLowerCase() {
632 return %StringToLowerCase(ToString(this));
633}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634
635
636// ECMA-262, 15.5.4.18
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000637function StringToUpperCase() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000638 return %StringToUpperCase(ToString(this));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000639}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000640
641
642// ECMA-262, 15.5.4.19
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000643function StringToLocaleUpperCase() {
644 return %StringToUpperCase(ToString(this));
645}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000646
647
648// ECMA-262, section 15.5.3.2
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000649function StringFromCharCode(code) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000650 var n = %_ArgumentsLength();
651 if (n == 1) return %CharFromCode(ToNumber(code) & 0xffff)
652
653 // NOTE: This is not super-efficient, but it is necessary because we
654 // want to avoid converting to numbers from within the virtual
655 // machine. Maybe we can find another way of doing this?
656 var codes = new $Array(n);
657 for (var i = 0; i < n; i++) codes[i] = ToNumber(%_Arguments(i));
658 return %StringFromCharCodeArray(codes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000659}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000660
661
662// Helper function for very basic XSS protection.
663function HtmlEscape(str) {
664 return ToString(str).replace(/</g, "&lt;")
665 .replace(/>/g, "&gt;")
666 .replace(/"/g, "&quot;")
667 .replace(/'/g, "&#039;");
668};
669
670
671// Compatibility support for KJS.
672// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000673function StringLink(s) {
674 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
675}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676
677
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000678function StringAnchor(name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000679 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000680}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000681
682
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000683function StringFontcolor(color) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000684 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000685}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000686
687
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000688function StringFontsize(size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000690}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691
692
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000693function StringBig() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000694 return "<big>" + this + "</big>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000695}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696
697
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000698function StringBlink() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000699 return "<blink>" + this + "</blink>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000700}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000701
702
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000703function StringBold() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000704 return "<b>" + this + "</b>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000705}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706
707
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000708function StringFixed() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000709 return "<tt>" + this + "</tt>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000710}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000711
712
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000713function StringItalics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 return "<i>" + this + "</i>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000715}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000716
717
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000718function StringSmall() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000719 return "<small>" + this + "</small>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000720}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000721
722
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000723function StringStrike() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 return "<strike>" + this + "</strike>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000725}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726
727
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000728function StringSub() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 return "<sub>" + this + "</sub>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000730}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731
732
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000733function StringSup() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000734 return "<sup>" + this + "</sup>";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000735}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736
737
738// StringBuilder support.
739
740function StringBuilder() {
741 this.elements = new $Array();
742}
743
744
745function ReplaceResultBuilder(str) {
746 this.elements = new $Array();
747 this.special_string = str;
748}
749
750
751ReplaceResultBuilder.prototype.add =
752StringBuilder.prototype.add = function(str) {
753 if (!IS_STRING(str)) str = ToString(str);
754 if (str.length > 0) {
755 var elements = this.elements;
756 elements[elements.length] = str;
757 }
758}
759
760
761ReplaceResultBuilder.prototype.addSpecialSlice = function(start, end) {
762 var len = end - start;
763 if (len == 0) return;
764 var elements = this.elements;
765 if (start >= 0 && len >= 0 && start < 0x80000 && len < 0x800) {
766 elements[elements.length] = (start << 11) + len;
767 } else {
768 elements[elements.length] = SubString(this.special_string, start, end);
769 }
770}
771
772
773StringBuilder.prototype.generate = function() {
774 return %StringBuilderConcat(this.elements, "");
775}
776
777
778ReplaceResultBuilder.prototype.generate = function() {
779 return %StringBuilderConcat(this.elements, this.special_string);
780}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000781
782
783// -------------------------------------------------------------------
784
785function SetupString() {
786 // Setup the constructor property on the String prototype object.
787 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
788
789
790 // Setup the non-enumerable functions on the String object.
791 InstallFunctions($String, DONT_ENUM, $Array(
792 "fromCharCode", StringFromCharCode
793 ));
794
795
796 // Setup the non-enumerable functions on the String prototype object.
797 InstallFunctions($String.prototype, DONT_ENUM, $Array(
798 "valueOf", StringValueOf,
799 "toString", StringToString,
800 "charAt", StringCharAt,
801 "charCodeAt", StringCharCodeAt,
802 "concat", StringConcat,
803 "indexOf", StringIndexOf,
804 "lastIndexOf", StringLastIndexOf,
805 "localeCompare", StringLocaleCompare,
806 "match", StringMatch,
807 "replace", StringReplace,
808 "search", StringSearch,
809 "slice", StringSlice,
810 "split", StringSplit,
811 "substring", StringSubstring,
812 "substr", StringSubstr,
813 "toLowerCase", StringToLowerCase,
814 "toLocaleLowerCase", StringToLocaleLowerCase,
815 "toUpperCase", StringToUpperCase,
816 "toLocaleUpperCase", StringToLocaleUpperCase,
817 "link", StringLink,
818 "anchor", StringAnchor,
819 "fontcolor", StringFontcolor,
820 "fontsize", StringFontsize,
821 "big", StringBig,
822 "blink", StringBlink,
823 "bold", StringBold,
824 "fixed", StringFixed,
825 "italics", StringItalics,
826 "small", StringSmall,
827 "strike", StringStrike,
828 "sub", StringSub,
829 "sup", StringSup
830 ));
831}
832
833
834SetupString();