[js_runtime] Implement String.replaceAll for general Patterns

Fixes: 48100

Change-Id: Iaefad9b236aa0deea4ced0c404ee7c30e6d23f1d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/226880
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
index 7a0de57..474042a 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
@@ -169,8 +169,15 @@
     var re = regExpGetGlobalNative(pattern);
     return stringReplaceJS(receiver, re, replacement);
   } else {
-    // TODO(floitsch): implement generic String.replace (with patterns).
-    throw "String.replaceAll(Pattern) UNIMPLEMENTED";
+    int startIndex = 0;
+    StringBuffer result = StringBuffer();
+    for (Match match in pattern.allMatches(receiver)) {
+      result.write(substring2Unchecked(receiver, startIndex, match.start));
+      result.write(replacement);
+      startIndex = match.end;
+    }
+    result.write(substring1Unchecked(receiver, startIndex));
+    return result.toString();
   }
 }
 
diff --git a/sdk/lib/_internal/js_runtime/lib/string_helper.dart b/sdk/lib/_internal/js_runtime/lib/string_helper.dart
index 5e6c50d..9305f55 100644
--- a/sdk/lib/_internal/js_runtime/lib/string_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/string_helper.dart
@@ -173,9 +173,21 @@
     return stringReplaceJS(receiver, re, replacement);
   }
 
+  return stringReplaceAllGeneral(receiver, pattern, replacement);
+}
+
+String stringReplaceAllGeneral(
+    String receiver, Pattern pattern, String replacement) {
   checkNull(pattern);
-  // TODO(floitsch): implement generic String.replace (with patterns).
-  throw "String.replaceAll(Pattern) UNIMPLEMENTED";
+  int startIndex = 0;
+  StringBuffer result = StringBuffer();
+  for (Match match in pattern.allMatches(receiver)) {
+    result.write(substring2Unchecked(receiver, startIndex, match.start));
+    result.write(replacement);
+    startIndex = match.end;
+  }
+  result.write(substring1Unchecked(receiver, startIndex));
+  return result.toString();
 }
 
 /// Replaces all non-overlapping occurences of [pattern] in [receiver] with