Revision: 4653
Author: [email protected]
Date: Thu May 13 05:13:27 2010
Log: Simplified replace JS.
Refactored code so global/non-global regexps are handled in separate functions.
Inlined ApplyReplaceFunction at its only call point.

Review URL: http://codereview.chromium.org/1994019
http://code.google.com/p/v8/source/detail?r=4653

Modified:
 /branches/bleeding_edge/src/string.js

=======================================
--- /branches/bleeding_edge/src/string.js       Wed Apr 21 01:33:04 2010
+++ /branches/bleeding_edge/src/string.js       Thu May 13 05:13:27 2010
@@ -241,7 +241,13 @@
     %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
     if (IS_FUNCTION(replace)) {
       regExpCache.type = 'none';
-      return StringReplaceRegExpWithFunction(subject, search, replace);
+      if (search.global) {
+ return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
+      } else {
+        return StringReplaceNonGlobalRegExpWithFunction(subject,
+                                                        search,
+                                                        replace);
+      }
     } else {
       return StringReplaceRegExp(subject, search, replace);
     }
@@ -396,9 +402,9 @@
   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)];
-  // If either start or end is missing return undefined.
-  if (start < 0 || end < 0) return;
   return SubString(string, start, end);
 };

@@ -410,9 +416,8 @@
   var scaled = index << 1;
   // Compute start and end.
   var start = matchInfo[CAPTURE(scaled)];
+  if (start < 0) return;
   var end = matchInfo[CAPTURE(scaled + 1)];
-  // If either start or end is missing return.
-  if (start < 0 || end <= start) return;
   builder.addSpecialSlice(start, end);
 };

@@ -423,111 +428,115 @@

 // Helper function for replacing regular expressions with the result of a
 // function application in String.prototype.replace.
-function StringReplaceRegExpWithFunction(subject, regexp, replace) {
-  if (regexp.global) {
-    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 = $Array(16);
-    }
-
-    var res = %RegExpExecMultiple(regexp,
-                                  subject,
-                                  lastMatchInfo,
-                                  resultArray);
-    regexp.lastIndex = 0;
-    if (IS_NULL(res)) {
-      // No matches at all.
-      return subject;
-    }
-    var len = res.length;
-    var i = 0;
-    if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
-      var match_start = 0;
-      var override = [null, 0, subject];
-      while (i < len) {
-        var elem = res[i];
-        if (%_IsSmi(elem)) {
-          if (elem > 0) {
-            match_start = (elem >> 11) + (elem & 0x7ff);
-          } else {
-            match_start = res[++i] - elem;
-          }
+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 = $Array(16);
+  }
+  var res = %RegExpExecMultiple(regexp,
+                                subject,
+                                lastMatchInfo,
+                                resultArray);
+  regexp.lastIndex = 0;
+  if (IS_NULL(res)) {
+    // No matches at all.
+    reusableReplaceArray = resultArray;
+    return subject;
+  }
+  var len = res.length;
+  var i = 0;
+  if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
+    var match_start = 0;
+    var override = [null, 0, subject];
+    var receiver = %GetGlobalReceiver();
+    while (i < len) {
+      var elem = res[i];
+      if (%_IsSmi(elem)) {
+        if (elem > 0) {
+          match_start = (elem >> 11) + (elem & 0x7ff);
         } else {
-          override[0] = elem;
-          override[1] = match_start;
-          lastMatchInfoOverride = override;
-          var func_result = replace.call(null, elem, match_start, subject);
-          if (!IS_STRING(func_result)) {
-            func_result = NonStringToString(func_result);
-          }
-          res[i] = func_result;
-          match_start += elem.length;
-        }
-        i++;
-      }
-    } else {
-      while (i < len) {
-        var elem = res[i];
-        if (!%_IsSmi(elem)) {
-          // elem must be an Array.
- // Use the apply argument as backing for global RegExp properties.
-          lastMatchInfoOverride = elem;
-          var func_result = replace.apply(null, elem);
-          if (!IS_STRING(func_result)) {
-            func_result = NonStringToString(func_result);
-          }
-          res[i] = func_result;
-        }
-        i++;
-      }
-    }
-    var resultBuilder = new ReplaceResultBuilder(subject, res);
-    var result = resultBuilder.generate();
-    resultArray.length = 0;
-    reusableReplaceArray = resultArray;
-    return result;
-  } else { // Not a global regexp, no need to loop.
-    var matchInfo = DoRegExpExec(regexp, subject, 0);
-    if (IS_NULL(matchInfo)) return subject;
-
-    var result = new ReplaceResultBuilder(subject);
-    result.addSpecialSlice(0, matchInfo[CAPTURE0]);
-    var endOfMatch = matchInfo[CAPTURE1];
-    result.add(ApplyReplacementFunction(replace, matchInfo, subject));
-    // Can't use matchInfo any more from here, since the function could
-    // overwrite it.
-    result.addSpecialSlice(endOfMatch, subject.length);
-    return result.generate();
-  }
+          match_start = res[++i] - elem;
+        }
+      } else {
+        override[0] = elem;
+        override[1] = match_start;
+        lastMatchInfoOverride = override;
+        var func_result =
+            %_CallFunction(receiver, elem, match_start, subject, replace);
+        if (!IS_STRING(func_result)) {
+          func_result = NonStringToString(func_result);
+        }
+        res[i] = func_result;
+        match_start += elem.length;
+      }
+      i++;
+    }
+  } else {
+    while (i < len) {
+      var elem = res[i];
+      if (!%_IsSmi(elem)) {
+        // elem must be an Array.
+        // Use the apply argument as backing for global RegExp properties.
+        lastMatchInfoOverride = elem;
+        var func_result = replace.apply(null, elem);
+        if (!IS_STRING(func_result)) {
+          func_result = NonStringToString(func_result);
+        }
+        res[i] = func_result;
+      }
+      i++;
+    }
+  }
+  var resultBuilder = new ReplaceResultBuilder(subject, res);
+  var result = resultBuilder.generate();
+  resultArray.length = 0;
+  reusableReplaceArray = resultArray;
+  return result;
 }


-// Helper function to apply a string replacement function once.
-function ApplyReplacementFunction(replace, matchInfo, subject) {
+function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
+  var matchInfo = DoRegExpExec(regexp, subject, 0);
+  if (IS_NULL(matchInfo)) return subject;
+  var result = new ReplaceResultBuilder(subject);
+  var index = matchInfo[CAPTURE0];
+  result.addSpecialSlice(0, index);
+  var endOfMatch = matchInfo[CAPTURE1];
   // Compute the parameter list consisting of the match, captures, index,
   // and subject for the replace function invocation.
-  var index = matchInfo[CAPTURE0];
   // The number of captures plus one for the match.
   var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
+  var replacement;
   if (m == 1) {
-    var s = CaptureString(subject, matchInfo, 0);
+    // 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.
-    return replace.call(null, s, index, subject);
-  }
-  var parameters = $Array(m + 2);
-  for (var j = 0; j < m; j++) {
-    parameters[j] = CaptureString(subject, matchInfo, j);
-  }
-  parameters[j] = index;
-  parameters[j + 1] = subject;
-  return replace.apply(null, parameters);
-}
+    replacement =
+        %_CallFunction(%GetGlobalReceiver(), s, index, subject, replace);
+  } else {
+    var parameters = $Array(m + 2);
+    for (var j = 0; j < m; j++) {
+      parameters[j] = CaptureString(subject, matchInfo, j);
+    }
+    parameters[j] = index;
+    parameters[j + 1] = subject;
+
+    replacement = replace.apply(null, parameters);
+  }
+
+ result.add(replacement); // The add method converts to string if necessary.
+  // Can't use matchInfo any more from here, since the function could
+  // overwrite it.
+  result.addSpecialSlice(endOfMatch, subject.length);
+  return result.generate();
+}
+

 // ECMA-262 section 15.5.4.12
 function StringSearch(re) {

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to