| Index: src/js/regexp.js | 
| diff --git a/src/js/regexp.js b/src/js/regexp.js | 
| index a163952451f082ea50e12678a7a94fffa3e4052e..3ca5c39811ea869449295a715298642ac0f0f790 100644 | 
| --- a/src/js/regexp.js | 
| +++ b/src/js/regexp.js | 
| @@ -9,6 +9,7 @@ | 
| // ------------------------------------------------------------------- | 
| // Imports | 
|  | 
| +var ExpandReplacement; | 
| var FLAG_harmony_tolength; | 
| var GlobalObject = global.Object; | 
| var GlobalRegExp = global.RegExp; | 
| @@ -17,6 +18,7 @@ var InternalArray = utils.InternalArray; | 
| var InternalPackedArray = utils.InternalPackedArray; | 
| var MakeTypeError; | 
| var matchSymbol = utils.ImportNow("match_symbol"); | 
| +var replaceSymbol = utils.ImportNow("replace_symbol"); | 
| var searchSymbol = utils.ImportNow("search_symbol"); | 
| var splitSymbol = utils.ImportNow("split_symbol"); | 
|  | 
| @@ -25,6 +27,7 @@ utils.ImportFromExperimental(function(from) { | 
| }); | 
|  | 
| utils.Import(function(from) { | 
| +  ExpandReplacement = from.ExpandReplacement; | 
| MakeTypeError = from.MakeTypeError; | 
| }); | 
|  | 
| @@ -379,6 +382,175 @@ function RegExpMatch(string) { | 
| } | 
|  | 
|  | 
| +// ES6 21.2.5.8. | 
| + | 
| +// TODO(lrn): This array will survive indefinitely if replace is never | 
| +// called again. However, it will be empty, since the contents are cleared | 
| +// in the finally block. | 
| +var reusableReplaceArray = new InternalArray(4); | 
| + | 
| +// Helper function for replacing regular expressions with the result of a | 
| +// function application in String.prototype.replace. | 
| +function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { | 
| +  var resultArray = reusableReplaceArray; | 
| +  if (resultArray) { | 
| +    reusableReplaceArray = null; | 
| +  } else { | 
| +    // Inside a nested replace (replace called from the replacement function | 
| +    // of another replace) or we have failed to set the reusable array | 
| +    // back due to an exception in a replacement function. Create a new | 
| +    // array to use in the future, or until the original is written back. | 
| +    resultArray = new InternalArray(16); | 
| +  } | 
| +  var res = %RegExpExecMultiple(regexp, | 
| +                                subject, | 
| +                                RegExpLastMatchInfo, | 
| +                                resultArray); | 
| +  regexp.lastIndex = 0; | 
| +  if (IS_NULL(res)) { | 
| +    // No matches at all. | 
| +    reusableReplaceArray = resultArray; | 
| +    return subject; | 
| +  } | 
| +  var len = res.length; | 
| +  if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) { | 
| +    // If the number of captures is two then there are no explicit captures in | 
| +    // the regexp, just the implicit capture that captures the whole match.  In | 
| +    // this case we can simplify quite a bit and end up with something faster. | 
| +    // The builder will consist of some integers that indicate slices of the | 
| +    // input string and some replacements that were returned from the replace | 
| +    // function. | 
| +    var match_start = 0; | 
| +    for (var i = 0; i < len; i++) { | 
| +      var elem = res[i]; | 
| +      if (%_IsSmi(elem)) { | 
| +        // Integers represent slices of the original string. | 
| +        if (elem > 0) { | 
| +          match_start = (elem >> 11) + (elem & 0x7ff); | 
| +        } else { | 
| +          match_start = res[++i] - elem; | 
| +        } | 
| +      } else { | 
| +        var func_result = replace(elem, match_start, subject); | 
| +        // Overwrite the i'th element in the results with the string we got | 
| +        // back from the callback function. | 
| +        res[i] = TO_STRING(func_result); | 
| +        match_start += elem.length; | 
| +      } | 
| +    } | 
| +  } else { | 
| +    for (var i = 0; i < len; i++) { | 
| +      var elem = res[i]; | 
| +      if (!%_IsSmi(elem)) { | 
| +        // elem must be an Array. | 
| +        // Use the apply argument as backing for global RegExp properties. | 
| +        var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length); | 
| +        // Overwrite the i'th element in the results with the string we got | 
| +        // back from the callback function. | 
| +        res[i] = TO_STRING(func_result); | 
| +      } | 
| +    } | 
| +  } | 
| +  var result = %StringBuilderConcat(res, len, subject); | 
| +  resultArray.length = 0; | 
| +  reusableReplaceArray = resultArray; | 
| +  return result; | 
| +} | 
| + | 
| + | 
| +// Compute the string of a given regular expression capture. | 
| +function CaptureString(string, lastCaptureInfo, index) { | 
| +  // Scale the index. | 
| +  var scaled = index << 1; | 
| +  // Compute start and end. | 
| +  var start = lastCaptureInfo[CAPTURE(scaled)]; | 
| +  // If start isn't valid, return undefined. | 
| +  if (start < 0) return; | 
| +  var end = lastCaptureInfo[CAPTURE(scaled + 1)]; | 
| +  return %_SubString(string, start, end); | 
| +} | 
| + | 
| + | 
| +function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { | 
| +  var matchInfo = DoRegExpExec(regexp, subject, 0); | 
| +  if (IS_NULL(matchInfo)) { | 
| +    regexp.lastIndex = 0; | 
| +    return subject; | 
| +  } | 
| +  var index = matchInfo[CAPTURE0]; | 
| +  var result = %_SubString(subject, 0, index); | 
| +  var endOfMatch = matchInfo[CAPTURE1]; | 
| +  // Compute the parameter list consisting of the match, captures, index, | 
| +  // and subject for the replace function invocation. | 
| +  // The number of captures plus one for the match. | 
| +  var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | 
| +  var replacement; | 
| +  if (m == 1) { | 
| +    // No captures, only the match, which is always valid. | 
| +    var s = %_SubString(subject, index, endOfMatch); | 
| +    // Don't call directly to avoid exposing the built-in global object. | 
| +    replacement = replace(s, index, subject); | 
| +  } else { | 
| +    var parameters = new InternalArray(m + 2); | 
| +    for (var j = 0; j < m; j++) { | 
| +      parameters[j] = CaptureString(subject, matchInfo, j); | 
| +    } | 
| +    parameters[j] = index; | 
| +    parameters[j + 1] = subject; | 
| + | 
| +    replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2); | 
| +  } | 
| + | 
| +  result += replacement;  // The add method converts to string if necessary. | 
| +  // Can't use matchInfo any more from here, since the function could | 
| +  // overwrite it. | 
| +  return result + %_SubString(subject, endOfMatch, subject.length); | 
| +} | 
| + | 
| + | 
| +function RegExpReplace(string, replace) { | 
| +  // TODO(littledan): allow non-regexp receivers. | 
| +  if (!IS_REGEXP(this)) { | 
| +    throw MakeTypeError(kIncompatibleMethodReceiver, | 
| +                        "RegExp.prototype.@@replace", this); | 
| +  } | 
| +  var subject = TO_STRING(string); | 
| +  var search = this; | 
| + | 
| +  if (!IS_CALLABLE(replace)) { | 
| +    replace = TO_STRING(replace); | 
| + | 
| +    if (!REGEXP_GLOBAL(search)) { | 
| +      // Non-global regexp search, string replace. | 
| +      var match = DoRegExpExec(search, subject, 0); | 
| +      if (match == null) { | 
| +        search.lastIndex = 0 | 
| +        return subject; | 
| +      } | 
| +      if (replace.length == 0) { | 
| +        return %_SubString(subject, 0, match[CAPTURE0]) + | 
| +               %_SubString(subject, match[CAPTURE1], subject.length) | 
| +      } | 
| +      return ExpandReplacement(replace, subject, RegExpLastMatchInfo, | 
| +                                 %_SubString(subject, 0, match[CAPTURE0])) + | 
| +             %_SubString(subject, match[CAPTURE1], subject.length); | 
| +    } | 
| + | 
| +    // Global regexp search, string replace. | 
| +    search.lastIndex = 0; | 
| +    return %StringReplaceGlobalRegExpWithString( | 
| +        subject, search, replace, RegExpLastMatchInfo); | 
| +  } | 
| + | 
| +  if (REGEXP_GLOBAL(search)) { | 
| +    // Global regexp search, function replace. | 
| +    return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | 
| +  } | 
| +  // Non-global regexp search, function replace. | 
| +  return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); | 
| +} | 
| + | 
| + | 
| // ES6 21.2.5.9. | 
| function RegExpSearch(string) { | 
| // TODO(yangguo): allow non-regexp receivers. | 
| @@ -511,6 +683,7 @@ utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ | 
| "toString", RegExpToString, | 
| "compile", RegExpCompileJS, | 
| matchSymbol, RegExpMatch, | 
| +  replaceSymbol, RegExpReplace, | 
| searchSymbol, RegExpSearch, | 
| splitSymbol, RegExpSplit, | 
| ]); | 
|  |