Early exit on VM String.replaceAll

Change-Id: I2578200444935d221fab7f9fb4d515a9b2e75937
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97145
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart
index 0fcb35c..e75284a 100644
--- a/runtime/lib/string_patch.dart
+++ b/runtime/lib/string_patch.dart
@@ -628,10 +628,13 @@
   String replaceAll(Pattern pattern, String replacement) {
     if (pattern == null) throw new ArgumentError.notNull("pattern");
     if (replacement == null) throw new ArgumentError.notNull("replacement");
-    List matches = [];
-    int length = 0;
-    int replacementLength = replacement.length;
+
     int startIndex = 0;
+    // String fragments that replace the the prefix [this] up to [startIndex].
+    List matches = [];
+    int length = 0; // Length of all fragments.
+    int replacementLength = replacement.length;
+
     if (replacementLength == 0) {
       for (Match match in pattern.allMatches(this)) {
         length += _addReplaceSlice(matches, startIndex, match.start);
@@ -645,6 +648,8 @@
         startIndex = match.end;
       }
     }
+    // No match, or a zero-length match at start with zero-length replacement.
+    if (startIndex == 0 && length == 0) return this;
     length += _addReplaceSlice(matches, startIndex, this.length);
     bool replacementIsOneByte = replacement._isOneByte;
     if (replacementIsOneByte &&