| Index: src/string.js
 | 
| diff --git a/src/string.js b/src/string.js
 | 
| index ba01ed67e855172a45951160f28ba4669af47153..49f403de7da8140bb8aeebd9d87c00c329850a0d 100644
 | 
| --- a/src/string.js
 | 
| +++ b/src/string.js
 | 
| @@ -34,7 +34,7 @@
 | 
|  
 | 
|  // Set the String function and constructor.
 | 
|  %SetCode($String, function(x) {
 | 
| -  var value = %_ArgumentsLength() == 0 ? '' : ToString(x);
 | 
| +  var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
 | 
|    if (%_IsConstructCall()) {
 | 
|      %_SetValueOf(this, value);
 | 
|    } else {
 | 
| @@ -64,7 +64,7 @@ function StringValueOf() {
 | 
|  function StringCharAt(pos) {
 | 
|    var char_code = %_FastCharCodeAt(this, pos);
 | 
|    if (!%_IsSmi(char_code)) {
 | 
| -    var subject = ToString(this);
 | 
| +    var subject = TO_STRING_INLINE(this);
 | 
|      var index = TO_INTEGER(pos);
 | 
|      if (index >= subject.length || index < 0) return "";
 | 
|      char_code = %StringCharCodeAt(subject, index);
 | 
| @@ -79,7 +79,7 @@ function StringCharCodeAt(pos) {
 | 
|    if (%_IsSmi(fast_answer)) {
 | 
|      return fast_answer;
 | 
|    }
 | 
| -  var subject = ToString(this);
 | 
| +  var subject = TO_STRING_INLINE(this);
 | 
|    var index = TO_INTEGER(pos);
 | 
|    return %StringCharCodeAt(subject, index);
 | 
|  }
 | 
| @@ -88,7 +88,7 @@ function StringCharCodeAt(pos) {
 | 
|  // ECMA-262, section 15.5.4.6
 | 
|  function StringConcat() {
 | 
|    var len = %_ArgumentsLength();
 | 
| -  var this_as_string = IS_STRING(this) ? this : ToString(this);
 | 
| +  var this_as_string = TO_STRING_INLINE(this);
 | 
|    if (len === 1) {
 | 
|      return this_as_string + %_Arguments(0);
 | 
|    }
 | 
| @@ -96,7 +96,7 @@ function StringConcat() {
 | 
|    parts[0] = this_as_string;
 | 
|    for (var i = 0; i < len; i++) {
 | 
|      var part = %_Arguments(i);
 | 
| -    parts[i + 1] = IS_STRING(part) ? part : ToString(part);
 | 
| +    parts[i + 1] = TO_STRING_INLINE(part);
 | 
|    }
 | 
|    return %StringBuilderConcat(parts, len + 1, "");
 | 
|  }
 | 
| @@ -107,8 +107,8 @@ function StringConcat() {
 | 
|  
 | 
|  // ECMA-262 section 15.5.4.7
 | 
|  function StringIndexOf(searchString /* position */) {  // length == 1
 | 
| -  var subject_str = ToString(this);
 | 
| -  var pattern_str = ToString(searchString);
 | 
| +  var subject_str = TO_STRING_INLINE(this);
 | 
| +  var pattern_str = TO_STRING_INLINE(searchString);
 | 
|    var subject_str_len = subject_str.length;
 | 
|    var pattern_str_len = pattern_str.length;
 | 
|    var index = 0;
 | 
| @@ -125,9 +125,9 @@ function StringIndexOf(searchString /* position */) {  // length == 1
 | 
|  
 | 
|  // ECMA-262 section 15.5.4.8
 | 
|  function StringLastIndexOf(searchString /* position */) {  // length == 1
 | 
| -  var sub = ToString(this);
 | 
| +  var sub = TO_STRING_INLINE(this);
 | 
|    var subLength = sub.length;
 | 
| -  var pat = ToString(searchString);
 | 
| +  var pat = TO_STRING_INLINE(searchString);
 | 
|    var patLength = pat.length;
 | 
|    var index = subLength - patLength;
 | 
|    if (%_ArgumentsLength() > 1) {
 | 
| @@ -156,8 +156,8 @@ function StringLastIndexOf(searchString /* position */) {  // length == 1
 | 
|  function StringLocaleCompare(other) {
 | 
|    if (%_ArgumentsLength() === 0) return 0;
 | 
|  
 | 
| -  var this_str = ToString(this);
 | 
| -  var other_str = ToString(other);
 | 
| +  var this_str = TO_STRING_INLINE(this);
 | 
| +  var other_str = TO_STRING_INLINE(other);
 | 
|    return %StringLocaleCompare(this_str, other_str);
 | 
|  }
 | 
|  
 | 
| @@ -165,7 +165,7 @@ function StringLocaleCompare(other) {
 | 
|  // ECMA-262 section 15.5.4.10
 | 
|  function StringMatch(regexp) {
 | 
|    if (!IS_REGEXP(regexp)) regexp = new ORIGINAL_REGEXP(regexp);
 | 
| -  var subject = ToString(this);
 | 
| +  var subject = TO_STRING_INLINE(this);
 | 
|  
 | 
|    if (!regexp.global) return regexp.exec(subject);
 | 
|    %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
 | 
| @@ -200,7 +200,7 @@ var reusableMatchInfo = [2, "", "", -1, -1];
 | 
|  
 | 
|  // ECMA-262, section 15.5.4.11
 | 
|  function StringReplace(search, replace) {
 | 
| -  var subject = IS_STRING(this) ? this : ToString(this);
 | 
| +  var subject = TO_STRING_INLINE(this);
 | 
|  
 | 
|    // Delegate to one of the regular expression variants if necessary.
 | 
|    if (IS_REGEXP(search)) {
 | 
| @@ -213,7 +213,7 @@ function StringReplace(search, replace) {
 | 
|    }
 | 
|  
 | 
|    // Convert the search argument to a string and search for it.
 | 
| -  search = IS_STRING(search) ? search : ToString(search);
 | 
| +  search = TO_STRING_INLINE(search);
 | 
|    var start = %StringIndexOf(subject, search, 0);
 | 
|    if (start < 0) return subject;
 | 
|    var end = start + search.length;
 | 
| @@ -228,7 +228,7 @@ function StringReplace(search, replace) {
 | 
|    } else {
 | 
|      reusableMatchInfo[CAPTURE0] = start;
 | 
|      reusableMatchInfo[CAPTURE1] = end;
 | 
| -    if (!IS_STRING(replace)) replace = ToString(replace);
 | 
| +    replace = TO_STRING_INLINE(replace);
 | 
|      ExpandReplacement(replace, subject, reusableMatchInfo, builder);
 | 
|    }
 | 
|  
 | 
| @@ -241,7 +241,7 @@ function StringReplace(search, replace) {
 | 
|  
 | 
|  // Helper function for regular expressions in String.prototype.replace.
 | 
|  function StringReplaceRegExp(subject, regexp, replace) {
 | 
| -  replace = ToString(replace);
 | 
| +  replace = TO_STRING_INLINE(replace);
 | 
|    return %StringReplaceRegExpWithString(subject,
 | 
|                                          regexp,
 | 
|                                          replace,
 | 
| @@ -462,7 +462,7 @@ function ApplyReplacementFunction(replace, matchInfo, subject) {
 | 
|  // ECMA-262 section 15.5.4.12
 | 
|  function StringSearch(re) {
 | 
|    var regexp = new ORIGINAL_REGEXP(re);
 | 
| -  var s = ToString(this);
 | 
| +  var s = TO_STRING_INLINE(this);
 | 
|    var last_idx = regexp.lastIndex; // keep old lastIndex
 | 
|    regexp.lastIndex = 0;            // ignore re.global property
 | 
|    var result = regexp.exec(s);
 | 
| @@ -476,7 +476,7 @@ function StringSearch(re) {
 | 
|  
 | 
|  // ECMA-262 section 15.5.4.13
 | 
|  function StringSlice(start, end) {
 | 
| -  var s = ToString(this);
 | 
| +  var s = TO_STRING_INLINE(this);
 | 
|    var s_len = s.length;
 | 
|    var start_i = TO_INTEGER(start);
 | 
|    var end_i = s_len;
 | 
| @@ -511,7 +511,7 @@ function StringSlice(start, end) {
 | 
|  
 | 
|  // ECMA-262 section 15.5.4.14
 | 
|  function StringSplit(separator, limit) {
 | 
| -  var subject = ToString(this);
 | 
| +  var subject = TO_STRING_INLINE(this);
 | 
|    limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
 | 
|    if (limit === 0) return [];
 | 
|  
 | 
| @@ -525,18 +525,35 @@ function StringSplit(separator, limit) {
 | 
|    }
 | 
|  
 | 
|    var length = subject.length;
 | 
| -  if (IS_REGEXP(separator)) {
 | 
| -    %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
 | 
| -  } else {
 | 
| -    separator = ToString(separator);
 | 
| +  if (!IS_REGEXP(separator)) {
 | 
| +    separator = TO_STRING_INLINE(separator);
 | 
| +    var separator_length = separator.length;
 | 
| +
 | 
|      // If the separator string is empty then return the elements in the subject.
 | 
| -    if (separator.length == 0) {
 | 
| +    if (separator_length === 0) {
 | 
|        var result = $Array(length);
 | 
|        for (var i = 0; i < length; i++) result[i] = subject[i];
 | 
|        return result;
 | 
|      }
 | 
| +
 | 
| +    var result = [];
 | 
| +    var start_index = 0;
 | 
| +    var index;
 | 
| +    while (true) {
 | 
| +      if (start_index + separator_length > length ||
 | 
| +          (index = %StringIndexOf(subject, separator, start_index)) === -1) {
 | 
| +        result.push(SubString(subject, start_index, length));
 | 
| +        break;
 | 
| +      }
 | 
| +      if (result.push(SubString(subject, start_index, index)) === limit) break;
 | 
| +      start_index = index + separator_length;
 | 
| +    }
 | 
| +
 | 
| +    return result;
 | 
|    }
 | 
|  
 | 
| +  %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
 | 
| +
 | 
|    if (length === 0) {
 | 
|      if (splitMatch(separator, subject, 0, 0) != null) return [];
 | 
|      return [subject];
 | 
| @@ -571,7 +588,8 @@ function StringSplit(separator, limit) {
 | 
|      result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]);
 | 
|      if (result.length === limit) return result;
 | 
|  
 | 
| -    for (var i = 2; i < NUMBER_OF_CAPTURES(matchInfo); i += 2) {
 | 
| +    var num_captures = NUMBER_OF_CAPTURES(matchInfo);
 | 
| +    for (var i = 2; i < num_captures; i += 2) {
 | 
|        var start = matchInfo[CAPTURE(i)];
 | 
|        var end = matchInfo[CAPTURE(i + 1)];
 | 
|        if (start != -1 && end != -1) {
 | 
| @@ -591,28 +609,18 @@ function StringSplit(separator, limit) {
 | 
|  // Helper function used by split.  This version returns the matchInfo
 | 
|  // instead of allocating a new array with basically the same information.
 | 
|  function splitMatch(separator, subject, current_index, start_index) {
 | 
| -  if (IS_REGEXP(separator)) {
 | 
| -    var matchInfo = DoRegExpExec(separator, subject, start_index);
 | 
| -    if (matchInfo == null) return null;
 | 
| -    // Section 15.5.4.14 paragraph two says that we do not allow zero length
 | 
| -    // matches at the end of the string.
 | 
| -    if (matchInfo[CAPTURE0] === subject.length) return null;
 | 
| -    return matchInfo;
 | 
| -  }
 | 
| -
 | 
| -  var separatorIndex = subject.indexOf(separator, start_index);
 | 
| -  if (separatorIndex === -1) return null;
 | 
| -
 | 
| -  reusableMatchInfo[CAPTURE0] = separatorIndex;
 | 
| -  reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length;
 | 
| -  return reusableMatchInfo;
 | 
| -};
 | 
| +  var matchInfo = DoRegExpExec(separator, subject, start_index);
 | 
| +  if (matchInfo == null) return null;
 | 
| +  // Section 15.5.4.14 paragraph two says that we do not allow zero length
 | 
| +  // matches at the end of the string.
 | 
| +  if (matchInfo[CAPTURE0] === subject.length) return null;
 | 
| +  return matchInfo;
 | 
| +}
 | 
|  
 | 
|  
 | 
|  // ECMA-262 section 15.5.4.15
 | 
|  function StringSubstring(start, end) {
 | 
| -  var s = this;
 | 
| -  if (!IS_STRING(s)) s = ToString(s);
 | 
| +  var s = TO_STRING_INLINE(this);
 | 
|    var s_len = s.length;
 | 
|  
 | 
|    var start_i = TO_INTEGER(start);
 | 
| @@ -643,7 +651,7 @@ function StringSubstring(start, end) {
 | 
|  
 | 
|  // This is not a part of ECMA-262.
 | 
|  function StringSubstr(start, n) {
 | 
| -  var s = ToString(this);
 | 
| +  var s = TO_STRING_INLINE(this);
 | 
|    var len;
 | 
|  
 | 
|    // Correct n: If not given, set to string length; if explicitly
 | 
| @@ -681,38 +689,38 @@ function StringSubstr(start, n) {
 | 
|  
 | 
|  // ECMA-262, 15.5.4.16
 | 
|  function StringToLowerCase() {
 | 
| -  return %StringToLowerCase(ToString(this));
 | 
| +  return %StringToLowerCase(TO_STRING_INLINE(this));
 | 
|  }
 | 
|  
 | 
|  
 | 
|  // ECMA-262, 15.5.4.17
 | 
|  function StringToLocaleLowerCase() {
 | 
| -  return %StringToLowerCase(ToString(this));
 | 
| +  return %StringToLowerCase(TO_STRING_INLINE(this));
 | 
|  }
 | 
|  
 | 
|  
 | 
|  // ECMA-262, 15.5.4.18
 | 
|  function StringToUpperCase() {
 | 
| -  return %StringToUpperCase(ToString(this));
 | 
| +  return %StringToUpperCase(TO_STRING_INLINE(this));
 | 
|  }
 | 
|  
 | 
|  
 | 
|  // ECMA-262, 15.5.4.19
 | 
|  function StringToLocaleUpperCase() {
 | 
| -  return %StringToUpperCase(ToString(this));
 | 
| +  return %StringToUpperCase(TO_STRING_INLINE(this));
 | 
|  }
 | 
|  
 | 
|  // ES5, 15.5.4.20
 | 
|  function StringTrim() {
 | 
| -  return %StringTrim(ToString(this), true, true);
 | 
| +  return %StringTrim(TO_STRING_INLINE(this), true, true);
 | 
|  }
 | 
|  
 | 
|  function StringTrimLeft() {
 | 
| -  return %StringTrim(ToString(this), true, false);
 | 
| +  return %StringTrim(TO_STRING_INLINE(this), true, false);
 | 
|  }
 | 
|  
 | 
|  function StringTrimRight() {
 | 
| -  return %StringTrim(ToString(this), false, true);
 | 
| +  return %StringTrim(TO_STRING_INLINE(this), false, true);
 | 
|  }
 | 
|  
 | 
|  // ECMA-262, section 15.5.3.2
 | 
| @@ -731,10 +739,10 @@ function StringFromCharCode(code) {
 | 
|  
 | 
|  // Helper function for very basic XSS protection.
 | 
|  function HtmlEscape(str) {
 | 
| -  return ToString(str).replace(/</g, "<")
 | 
| -                      .replace(/>/g, ">")
 | 
| -                      .replace(/"/g, """)
 | 
| -                      .replace(/'/g, "'");
 | 
| +  return TO_STRING_INLINE(str).replace(/</g, "<")
 | 
| +                              .replace(/>/g, ">")
 | 
| +                              .replace(/"/g, """)
 | 
| +                              .replace(/'/g, "'");
 | 
|  };
 | 
|  
 | 
|  
 | 
| @@ -813,7 +821,7 @@ function ReplaceResultBuilder(str) {
 | 
|  
 | 
|  
 | 
|  ReplaceResultBuilder.prototype.add = function(str) {
 | 
| -  if (!IS_STRING(str)) str = ToString(str);
 | 
| +  str = TO_STRING_INLINE(str);
 | 
|    if (str.length > 0) {
 | 
|      var elements = this.elements;
 | 
|      elements[elements.length] = str;
 | 
| 
 |