The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / string.js @ 40c0f755

History | View | Annotate | Download (25 KB)

1 40c0f755 Ryan
// Copyright 2006-2008 the V8 project authors. All rights reserved.
2
// 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) {
37
  var value = %_ArgumentsLength() == 0 ? '' : ToString(x);
38
  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
48
function StringToString() {
49
  if (!IS_STRING(this) && !%HasStringClass(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
56
function StringValueOf() {
57
  if (!IS_STRING(this) && !%HasStringClass(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
64
function StringCharAt(pos) {
65
  var char_code = %_FastCharCodeAt(this, index);
66
  if (!%_IsSmi(char_code)) {
67
    var subject = ToString(this);
68
    var index = TO_INTEGER(pos);
69
    if (index >= subject.length || index < 0) return "";
70
    char_code = %StringCharCodeAt(subject, index);
71
  }
72
  return %CharFromCode(char_code);
73
}
74
75
76
// ECMA-262 section 15.5.4.5
77
function StringCharCodeAt(pos) {
78
  var fast_answer = %_FastCharCodeAt(this, pos);
79
  if (%_IsSmi(fast_answer)) {
80
    return fast_answer;
81
  }
82
  var subject = ToString(this);
83
  var index = TO_INTEGER(pos);
84
  return %StringCharCodeAt(subject, index);
85
}
86
87
88
// ECMA-262, section 15.5.4.6
89
function StringConcat() {
90
  var len = %_ArgumentsLength();
91
  var parts = new $Array(len + 1);
92
  parts[0] = ToString(this);
93
  for (var i = 0; i < len; i++)
94
    parts[i + 1] = ToString(%_Arguments(i));
95
  return parts.join('');
96
}
97
98
// Match ES3 and Safari
99
%FunctionSetLength(StringConcat, 1);
100
101
102
// ECMA-262 section 15.5.4.7
103
function StringIndexOf(searchString /* position */) {  // length == 1
104
  var subject_str = ToString(this);
105
  var pattern_str = ToString(searchString);
106
  var subject_str_len = subject_str.length;
107
  var pattern_str_len = pattern_str.length;
108
  var index = 0;
109
  if (%_ArgumentsLength() > 1) {
110
    var arg1 = %_Arguments(1);  // position
111
    index = TO_INTEGER(arg1);
112
  }
113
  if (index < 0) index = 0;
114
  if (index > subject_str_len) index = subject_str_len;
115
  if (pattern_str_len + index > subject_str_len) return -1;
116
  return %StringIndexOf(subject_str, pattern_str, index);
117
}
118
119
120
// ECMA-262 section 15.5.4.8
121
function StringLastIndexOf(searchString /* position */) {  // length == 1
122
  var sub = ToString(this);
123
  var pat = ToString(searchString);
124
  var index = (%_ArgumentsLength() > 1)
125
      ? ToNumber(%_Arguments(1) /* position */)
126
      : $NaN;
127
  var firstIndex;
128
  if ($isNaN(index)) {
129
    firstIndex = sub.length - pat.length;
130
  } else {
131
    firstIndex = TO_INTEGER(index);
132
    if (firstIndex + pat.length > sub.length) {
133
      firstIndex = sub.length - pat.length;
134
    }
135
  }
136
  return %StringLastIndexOf(sub, pat, firstIndex);
137
}
138
139
140
// ECMA-262 section 15.5.4.9
141
//
142
// This function is implementation specific.  For now, we do not
143
// do anything locale specific.
144
function StringLocaleCompare(other) {
145
  if (%_ArgumentsLength() === 0) return 0;
146
147
  var this_str = ToString(this);
148
  var other_str = ToString(other);
149
  return %StringLocaleCompare(this_str, other_str);
150
}
151
152
153
// ECMA-262 section 15.5.4.10
154
function StringMatch(regexp) {
155
  if (!IS_REGEXP(regexp)) regexp = new ORIGINAL_REGEXP(regexp);
156
  var subject = ToString(this);
157
158
  if (!regexp.global) return regexp.exec(subject);
159
  %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
160
  // lastMatchInfo is defined in regexp-delay.js.
161
  return %StringMatch(subject, regexp, lastMatchInfo);
162
}
163
164
165
// SubString is an internal function that returns the sub string of 'string'.
166
// If resulting string is of length 1, we use the one character cache
167
// otherwise we call the runtime system.
168
function SubString(string, start, end) {
169
  // Use the one character string cache.
170
  if (start + 1 == end) {
171
    var char_code = %_FastCharCodeAt(string, start);
172
    if (!%_IsSmi(char_code)) {
173
      char_code = %StringCharCodeAt(string, start);
174
    }
175
    return %CharFromCode(char_code);
176
  }
177
  return %StringSlice(string, start, end);
178
}
179
180
181
// ECMA-262, section 15.5.4.11
182
function StringReplace(search, replace) {
183
  var subject = ToString(this);
184
185
  // Delegate to one of the regular expression variants if necessary.
186
  if (IS_REGEXP(search)) {
187
    %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
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 ReplaceResultBuilder(subject);
202
  // prefix
203
  builder.addSpecialSlice(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
    reusableMatchInfo[CAPTURE0] = start;
210
    reusableMatchInfo[CAPTURE1] = end;
211
    ExpandReplacement(ToString(replace), subject, reusableMatchInfo, builder);
212
  }
213
214
  // suffix
215
  builder.addSpecialSlice(end, subject.length);
216
217
  return builder.generate();
218
}
219
220
221
// This has the same size as the lastMatchInfo array, and can be used for
222
// functions that expect that structure to be returned.  It is used when the
223
// needle is a string rather than a regexp.  In this case we can't update
224
// lastMatchArray without erroneously affecting the properties on the global
225
// RegExp object.
226
var reusableMatchInfo = [2, "", "", -1, -1];
227
228
229
// Helper function for regular expressions in String.prototype.replace.
230
function StringReplaceRegExp(subject, regexp, replace) {
231
  replace = ToString(replace);
232
  return %StringReplaceRegExpWithString(subject,
233
                                        regexp,
234
                                        replace,
235
                                        lastMatchInfo);
236
};
237
238
239
// Expand the $-expressions in the string and return a new string with
240
// the result.
241
function ExpandReplacement(string, subject, matchInfo, builder) {
242
  var next = %StringIndexOf(string, '$', 0);
243
  if (next < 0) {
244
    builder.add(string);
245
    return;
246
  }
247
248
  // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
249
  var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;  // Includes the match.
250
251
  if (next > 0) builder.add(SubString(string, 0, next));
252
  var length = string.length;
253
254
  while (true) {
255
    var expansion = '$';
256
    var position = next + 1;
257
    if (position < length) {
258
      var peek = %_FastCharCodeAt(string, position);
259
      if (!%_IsSmi(peek)) {
260
        peek = %StringCharCodeAt(string, position);
261
      }
262
      if (peek == 36) {         // $$
263
        ++position;
264
        builder.add('$');
265
      } else if (peek == 38) {  // $& - match
266
        ++position;
267
        builder.addSpecialSlice(matchInfo[CAPTURE0],
268
                                matchInfo[CAPTURE1]);
269
      } else if (peek == 96) {  // $` - prefix
270
        ++position;
271
        builder.addSpecialSlice(0, matchInfo[CAPTURE0]);
272
      } else if (peek == 39) {  // $' - suffix
273
        ++position;
274
        builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length);
275
      } else if (peek >= 48 && peek <= 57) {  // $n, 0 <= n <= 9
276
        ++position;
277
        var n = peek - 48;
278
        if (position < length) {
279
          peek = %_FastCharCodeAt(string, position);
280
          if (!%_IsSmi(peek)) {
281
            peek = %StringCharCodeAt(string, position);
282
          }
283
          // $nn, 01 <= nn <= 99
284
          if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
285
            var nn = n * 10 + (peek - 48);
286
            if (nn < m) {
287
              // If the two digit capture reference is within range of
288
              // the captures, we use it instead of the single digit
289
              // one. Otherwise, we fall back to using the single
290
              // digit reference. This matches the behavior of
291
              // SpiderMonkey.
292
              ++position;
293
              n = nn;
294
            }
295
          }
296
        }
297
        if (0 < n && n < m) {
298
          addCaptureString(builder, matchInfo, n);
299
        } else {
300
          // Because of the captures range check in the parsing of two
301
          // digit capture references, we can only enter here when a
302
          // single digit capture reference is outside the range of
303
          // captures.
304
          builder.add('$');
305
          --position;
306
        }
307
      } else {
308
        builder.add('$');
309
      }
310
    } else {
311
      builder.add('$');
312
    }
313
314
    // Go the the next $ in the string.
315
    next = %StringIndexOf(string, '$', position);
316
317
    // Return if there are no more $ characters in the string. If we
318
    // haven't reached the end, we need to append the suffix.
319
    if (next < 0) {
320
      if (position < length) {
321
        builder.add(SubString(string, position, length));
322
      }
323
      return;
324
    }
325
326
    // Append substring between the previous and the next $ character.
327
    builder.add(SubString(string, position, next));
328
  }
329
};
330
331
332
// Compute the string of a given regular expression capture.
333
function CaptureString(string, lastCaptureInfo, index) {
334
  // Scale the index.
335
  var scaled = index << 1;
336
  // Compute start and end.
337
  var start = lastCaptureInfo[CAPTURE(scaled)];
338
  var end = lastCaptureInfo[CAPTURE(scaled + 1)];
339
  // If either start or end is missing return undefined.
340
  if (start < 0 || end < 0) return;
341
  return SubString(string, start, end);
342
};
343
344
345
// Add the string of a given regular expression capture to the
346
// ReplaceResultBuilder
347
function addCaptureString(builder, matchInfo, index) {
348
  // Scale the index.
349
  var scaled = index << 1;
350
  // Compute start and end.
351
  var start = matchInfo[CAPTURE(scaled)];
352
  var end = matchInfo[CAPTURE(scaled + 1)];
353
  // If either start or end is missing return.
354
  if (start < 0 || end <= start) return;
355
  builder.addSpecialSlice(start, end);
356
};
357
358
359
// Helper function for replacing regular expressions with the result of a
360
// function application in String.prototype.replace.  The function application
361
// must be interleaved with the regexp matching (contrary to ECMA-262
362
// 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses
363
// the static properties of the RegExp constructor.  Example:
364
//     'abcd'.replace(/(.)/g, function() { return RegExp.$1; }
365
// should be 'abcd' and not 'dddd' (or anything else).
366
function StringReplaceRegExpWithFunction(subject, regexp, replace) {
367
  var result = new ReplaceResultBuilder(subject);
368
  var lastMatchInfo = DoRegExpExec(regexp, subject, 0);
369
  if (IS_NULL(lastMatchInfo)) return subject;
370
371
  // There's at least one match.  If the regexp is global, we have to loop
372
  // over all matches.  The loop is not in C++ code here like the one in
373
  // RegExp.prototype.exec, because of the interleaved function application.
374
  // Unfortunately, that means this code is nearly duplicated, here and in
375
  // jsregexp.cc.
376
  if (regexp.global) {
377
    var previous = 0;
378
    do {
379
      result.addSpecialSlice(previous, lastMatchInfo[CAPTURE0]);
380
      var startOfMatch = lastMatchInfo[CAPTURE0];
381
      previous = lastMatchInfo[CAPTURE1];
382
      result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
383
      // Can't use lastMatchInfo any more from here, since the function could
384
      // overwrite it.
385
      // Continue with the next match.
386
      // Increment previous if we matched an empty string, as per ECMA-262
387
      // 15.5.4.10.
388
      if (previous == startOfMatch) {
389
        // Add the skipped character to the output, if any.
390
        if (previous < subject.length) {
391
          result.addSpecialSlice(previous, previous + 1);
392
        }
393
        previous++;
394
      }
395
396
      // Per ECMA-262 15.10.6.2, if the previous index is greater than the
397
      // string length, there is no match
398
      lastMatchInfo = (previous > subject.length)
399
          ? null
400
          : DoRegExpExec(regexp, subject, previous);
401
    } while (!IS_NULL(lastMatchInfo));
402
403
    // Tack on the final right substring after the last match, if necessary.
404
    if (previous < subject.length) {
405
      result.addSpecialSlice(previous, subject.length);
406
    }
407
  } else { // Not a global regexp, no need to loop.
408
    result.addSpecialSlice(0, lastMatchInfo[CAPTURE0]);
409
    var endOfMatch = lastMatchInfo[CAPTURE1];
410
    result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
411
    // Can't use lastMatchInfo any more from here, since the function could
412
    // overwrite it.
413
    result.addSpecialSlice(endOfMatch, subject.length);
414
  }
415
416
  return result.generate();
417
}
418
419
420
// Helper function to apply a string replacement function once.
421
function ApplyReplacementFunction(replace, lastMatchInfo, subject) {
422
  // Compute the parameter list consisting of the match, captures, index,
423
  // and subject for the replace function invocation.
424
  var index = lastMatchInfo[CAPTURE0];
425
  // The number of captures plus one for the match.
426
  var m = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
427
  if (m == 1) {
428
    var s = CaptureString(subject, lastMatchInfo, 0);
429
    // Don't call directly to avoid exposing the built-in global object.
430
    return ToString(replace.call(null, s, index, subject));
431
  }
432
  var parameters = $Array(m + 2);
433
  for (var j = 0; j < m; j++) {
434
    parameters[j] = CaptureString(subject, lastMatchInfo, j);
435
  }
436
  parameters[j] = index;
437
  parameters[j + 1] = subject;
438
  return ToString(replace.apply(null, parameters));
439
}
440
441
442
// ECMA-262 section 15.5.4.12
443
function StringSearch(re) {
444
  var regexp = new ORIGINAL_REGEXP(re);
445
  var s = ToString(this);
446
  var last_idx = regexp.lastIndex; // keep old lastIndex
447
  regexp.lastIndex = 0;            // ignore re.global property
448
  var result = regexp.exec(s);
449
  regexp.lastIndex = last_idx;     // restore lastIndex
450
  if (result == null)
451
    return -1;
452
  else
453
    return result.index;
454
}
455
456
457
// ECMA-262 section 15.5.4.13
458
function StringSlice(start, end) {
459
  var s = ToString(this);
460
  var s_len = s.length;
461
  var start_i = TO_INTEGER(start);
462
  var end_i = s_len;
463
  if (end !== void 0)
464
    end_i = TO_INTEGER(end);
465
466
  if (start_i < 0) {
467
    start_i += s_len;
468
    if (start_i < 0)
469
      start_i = 0;
470
  } else {
471
    if (start_i > s_len)
472
      start_i = s_len;
473
  }
474
475
  if (end_i < 0) {
476
    end_i += s_len;
477
    if (end_i < 0)
478
      end_i = 0;
479
  } else {
480
    if (end_i > s_len)
481
      end_i = s_len;
482
  }
483
484
  var num_c = end_i - start_i;
485
  if (num_c < 0)
486
    num_c = 0;
487
488
  return SubString(s, start_i, start_i + num_c);
489
}
490
491
492
// ECMA-262 section 15.5.4.14
493
function StringSplit(separator, limit) {
494
  var subject = ToString(this);
495
  var result = [];
496
  var lim = (limit === void 0) ? 0xffffffff : ToUint32(limit);
497
498
  if (lim === 0) return result;
499
500
  // ECMA-262 says that if separator is undefined, the result should
501
  // be an array of size 1 containing the entire string.  SpiderMonkey
502
  // and KJS have this behaviour only when no separator is given.  If
503
  // undefined is explicitly given, they convert it to a string and
504
  // use that.  We do as SpiderMonkey and KJS.
505
  if (%_ArgumentsLength() === 0) {
506
    result[result.length] = subject;
507
    return result;
508
  }
509
510
  var length = subject.length;
511
  var currentIndex = 0;
512
  var startIndex = 0;
513
514
  var sep;
515
  if (IS_REGEXP(separator)) {
516
    sep = separator;
517
    %_Log('regexp', 'regexp-split,%0S,%1r', [subject, sep]);
518
  } else {
519
    sep = ToString(separator);
520
  }
521
522
  if (length === 0) {
523
    if (splitMatch(sep, subject, 0, 0) != null) return result;
524
    result[result.length] = subject;
525
    return result;
526
  }
527
528
  while (true) {
529
530
    if (startIndex === length) {
531
      result[result.length] = subject.slice(currentIndex, length);
532
      return result;
533
    }
534
535
    var lastMatchInfo = splitMatch(sep, subject, currentIndex, startIndex);
536
537
    if (IS_NULL(lastMatchInfo)) {
538
      result[result.length] = subject.slice(currentIndex, length);
539
      return result;
540
    }
541
542
    var endIndex = lastMatchInfo[CAPTURE1];
543
544
    // We ignore a zero-length match at the currentIndex.
545
    if (startIndex === endIndex && endIndex === currentIndex) {
546
      startIndex++;
547
      continue;
548
    }
549
550
    result[result.length] =
551
        SubString(subject, currentIndex, lastMatchInfo[CAPTURE0]);
552
    if (result.length === lim) return result;
553
554
    for (var i = 2; i < NUMBER_OF_CAPTURES(lastMatchInfo); i += 2) {
555
      var start = lastMatchInfo[CAPTURE(i)];
556
      var end = lastMatchInfo[CAPTURE(i + 1)];
557
      if (start != -1 && end != -1) {
558
        result[result.length] = SubString(subject,
559
                                          lastMatchInfo[CAPTURE(i)],
560
                                          lastMatchInfo[CAPTURE(i + 1)]);
561
      } else {
562
        result[result.length] = void 0;
563
      }
564
      if (result.length === lim) return result;
565
    }
566
567
    startIndex = currentIndex = endIndex;
568
  }
569
}
570
571
572
// ECMA-262 section 15.5.4.14
573
// Helper function used by split.  This version returns the lastMatchInfo
574
// instead of allocating a new array with basically the same information.
575
function splitMatch(separator, subject, current_index, start_index) {
576
  if (IS_REGEXP(separator)) {
577
    var lastMatchInfo = DoRegExpExec(separator, subject, start_index);
578
    if (lastMatchInfo == null) return null;
579
    // Section 15.5.4.14 paragraph two says that we do not allow zero length
580
    // matches at the end of the string.
581
    if (lastMatchInfo[CAPTURE0] === subject.length) return null;
582
    return lastMatchInfo;
583
  }
584
585
  var separatorIndex = subject.indexOf(separator, start_index);
586
  if (separatorIndex === -1) return null;
587
588
  reusableMatchInfo[CAPTURE0] = separatorIndex;
589
  reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length;
590
  return reusableMatchInfo;
591
};
592
593
594
// ECMA-262 section 15.5.4.15
595
function StringSubstring(start, end) {
596
  var s = ToString(this);
597
  var s_len = s.length;
598
  var start_i = TO_INTEGER(start);
599
  var end_i = s_len;
600
  if (!IS_UNDEFINED(end))
601
    end_i = TO_INTEGER(end);
602
603
  if (start_i < 0) start_i = 0;
604
  if (start_i > s_len) start_i = s_len;
605
  if (end_i < 0) end_i = 0;
606
  if (end_i > s_len) end_i = s_len;
607
608
  if (start_i > end_i) {
609
    var tmp = end_i;
610
    end_i = start_i;
611
    start_i = tmp;
612
  }
613
614
  return SubString(s, start_i, end_i);
615
}
616
617
618
// This is not a part of ECMA-262.
619
function StringSubstr(start, n) {
620
  var s = ToString(this);
621
  var len;
622
623
  // Correct n: If not given, set to string length; if explicitly
624
  // set to undefined, zero, or negative, returns empty string.
625
  if (n === void 0) {
626
    len = s.length;
627
  } else {
628
    len = TO_INTEGER(n);
629
    if (len <= 0) return '';
630
  }
631
632
  // Correct start: If not given (or undefined), set to zero; otherwise
633
  // convert to integer and handle negative case.
634
  if (start === void 0) {
635
    start = 0;
636
  } else {
637
    start = TO_INTEGER(start);
638
    // If positive, and greater than or equal to the string length,
639
    // return empty string.
640
    if (start >= s.length) return '';
641
    // If negative and absolute value is larger than the string length,
642
    // use zero.
643
    if (start < 0) {
644
      start += s.length;
645
      if (start < 0) start = 0;
646
    }
647
  }
648
649
  var end = start + len;
650
  if (end > s.length) end = s.length;
651
652
  return SubString(s, start, end);
653
}
654
655
656
// ECMA-262, 15.5.4.16
657
function StringToLowerCase() {
658
  return %StringToLowerCase(ToString(this));
659
}
660
661
662
// ECMA-262, 15.5.4.17
663
function StringToLocaleLowerCase() {
664
  return %StringToLowerCase(ToString(this));
665
}
666
667
668
// ECMA-262, 15.5.4.18
669
function StringToUpperCase() {
670
  return %StringToUpperCase(ToString(this));
671
}
672
673
674
// ECMA-262, 15.5.4.19
675
function StringToLocaleUpperCase() {
676
  return %StringToUpperCase(ToString(this));
677
}
678
679
680
// ECMA-262, section 15.5.3.2
681
function StringFromCharCode(code) {
682
  var n = %_ArgumentsLength();
683
  if (n == 1) return %CharFromCode(ToNumber(code) & 0xffff)
684
685
  // NOTE: This is not super-efficient, but it is necessary because we
686
  // want to avoid converting to numbers from within the virtual
687
  // machine. Maybe we can find another way of doing this?
688
  var codes = new $Array(n);
689
  for (var i = 0; i < n; i++) codes[i] = ToNumber(%_Arguments(i));
690
  return %StringFromCharCodeArray(codes);
691
}
692
693
694
// Helper function for very basic XSS protection.
695
function HtmlEscape(str) {
696
  return ToString(str).replace(/</g, "&lt;")
697
                      .replace(/>/g, "&gt;")
698
                      .replace(/"/g, "&quot;")
699
                      .replace(/'/g, "&#039;");
700
};
701
702
703
// Compatibility support for KJS.
704
// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
705
function StringLink(s) {
706
  return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
707
}
708
709
710
function StringAnchor(name) {
711
  return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
712
}
713
714
715
function StringFontcolor(color) {
716
  return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
717
}
718
719
720
function StringFontsize(size) {
721
  return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
722
}
723
724
725
function StringBig() {
726
  return "<big>" + this + "</big>";
727
}
728
729
730
function StringBlink() {
731
  return "<blink>" + this + "</blink>";
732
}
733
734
735
function StringBold() {
736
  return "<b>" + this + "</b>";
737
}
738
739
740
function StringFixed() {
741
  return "<tt>" + this + "</tt>";
742
}
743
744
745
function StringItalics() {
746
  return "<i>" + this + "</i>";
747
}
748
749
750
function StringSmall() {
751
  return "<small>" + this + "</small>";
752
}
753
754
755
function StringStrike() {
756
  return "<strike>" + this + "</strike>";
757
}
758
759
760
function StringSub() {
761
  return "<sub>" + this + "</sub>";
762
}
763
764
765
function StringSup() {
766
  return "<sup>" + this + "</sup>";
767
}
768
769
770
// StringBuilder support.
771
772
function StringBuilder() {
773
  this.elements = new $Array();
774
}
775
776
777
function ReplaceResultBuilder(str) {
778
  this.elements = new $Array();
779
  this.special_string = str;
780
}
781
782
783
ReplaceResultBuilder.prototype.add =
784
StringBuilder.prototype.add = function(str) {
785
  if (!IS_STRING(str)) str = ToString(str);
786
  if (str.length > 0) {
787
    var elements = this.elements;
788
    elements[elements.length] = str;
789
  }
790
}
791
792
793
ReplaceResultBuilder.prototype.addSpecialSlice = function(start, end) {
794
  var len = end - start;
795
  if (len == 0) return;
796
  var elements = this.elements;
797
  if (start >= 0 && len >= 0 && start < 0x80000 && len < 0x800) {
798
    elements[elements.length] = (start << 11) + len;
799
  } else {
800
    elements[elements.length] = SubString(this.special_string, start, end);
801
  }
802
}
803
804
805
StringBuilder.prototype.generate = function() {
806
  return %StringBuilderConcat(this.elements, "");
807
}
808
809
810
ReplaceResultBuilder.prototype.generate = function() {
811
  return %StringBuilderConcat(this.elements, this.special_string);
812
}
813
814
815
// -------------------------------------------------------------------
816
817
function SetupString() {
818
  // Setup the constructor property on the String prototype object.
819
  %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
820
821
822
  // Setup the non-enumerable functions on the String object.
823
  InstallFunctions($String, DONT_ENUM, $Array(
824
    "fromCharCode", StringFromCharCode
825
  ));
826
827
828
  // Setup the non-enumerable functions on the String prototype object.
829
  InstallFunctions($String.prototype, DONT_ENUM, $Array(
830
    "valueOf", StringValueOf,
831
    "toString", StringToString,
832
    "charAt", StringCharAt,
833
    "charCodeAt", StringCharCodeAt,
834
    "concat", StringConcat,
835
    "indexOf", StringIndexOf,
836
    "lastIndexOf", StringLastIndexOf,
837
    "localeCompare", StringLocaleCompare,
838
    "match", StringMatch,
839
    "replace", StringReplace,
840
    "search", StringSearch,
841
    "slice", StringSlice,
842
    "split", StringSplit,
843
    "substring", StringSubstring,
844
    "substr", StringSubstr,
845
    "toLowerCase", StringToLowerCase,
846
    "toLocaleLowerCase", StringToLocaleLowerCase,
847
    "toUpperCase", StringToUpperCase,
848
    "toLocaleUpperCase", StringToLocaleUpperCase,
849
    "link", StringLink,
850
    "anchor", StringAnchor,
851
    "fontcolor", StringFontcolor,
852
    "fontsize", StringFontsize,
853
    "big", StringBig,
854
    "blink", StringBlink,
855
    "bold", StringBold,
856
    "fixed", StringFixed,
857
    "italics", StringItalics,
858
    "small", StringSmall,
859
    "strike", StringStrike,
860
    "sub", StringSub,
861
    "sup", StringSup
862
  ));
863
}
864
865
866
SetupString();