[convert] Use s.substring(start, end) directly and relax identical() check on wasm
diff --git a/pkgs/convert/lib/src/string_replacer.dart b/pkgs/convert/lib/src/string_replacer.dart
index 0c45f66..1c7c95d 100644
--- a/pkgs/convert/lib/src/string_replacer.dart
+++ b/pkgs/convert/lib/src/string_replacer.dart
@@ -113,7 +113,7 @@
   ]) {
     end = RangeError.checkValidRange(start, end, input.length);
     if (replacements.isEmpty) {
-      return _substring(input, start, end);
+      return input.substring(start, end);
     }
     if (_isWeb || end - start < 64 || replacements.length == 1) {
       return _replaceOneShotIndexOf(input, replacements, start, end);
@@ -135,7 +135,7 @@
   @override
   String convert(String input, [int start = 0, int? end]) {
     end = RangeError.checkValidRange(start, end, input.length);
-    return _substring(input, start, end);
+    return input.substring(start, end);
   }
 
   @override
@@ -177,7 +177,7 @@
       i++;
     }
     if (i == end) {
-      return _substring(input, start, end);
+      return input.substring(start, end);
     }
 
     final out = StringBuffer();
@@ -235,7 +235,7 @@
       i++;
     }
     if (i == end) {
-      return _substring(input, start, end);
+      return input.substring(start, end);
     }
 
     StringBuffer? out;
@@ -259,7 +259,7 @@
     }
 
     if (out == null) {
-      return _substring(input, start, end);
+      return input.substring(start, end);
     }
     if (end > lastFlush) {
       out.write(input.substring(lastFlush, end));
@@ -409,7 +409,7 @@
   }
 
   if (activeKeys == null) {
-    return _substring(s, start, end);
+    return s.substring(start, end);
   }
 
   return _finishActiveKeyReplace(
@@ -463,7 +463,7 @@
   }
 
   if (activeKeys == null) {
-    return _substring(s, start, end);
+    return s.substring(start, end);
   }
 
   return _finishActiveKeyReplace(
@@ -478,12 +478,6 @@
   );
 }
 
-/// Returns [s] unchanged when the slice covers the entire string, avoiding a
-/// `JSStringImpl` wrapper allocation on `dart2wasm` where
-/// `substring(0, length)` does not return `this`.
-String _substring(String s, int start, int end) =>
-    (start == 0 && end == s.length) ? s : s.substring(start, end);
-
 /// Repeatedly emits the earliest (and longest on ties) match among
 /// [activeKeys], lazily advancing `positions[k]` via `s.indexOf` only for keys
 /// whose cached match position fell behind `lastFlush`, and dropping keys from
diff --git a/pkgs/convert/test/string_replacer_test.dart b/pkgs/convert/test/string_replacer_test.dart
index 49af7c8..4fe2dde 100644
--- a/pkgs/convert/test/string_replacer_test.dart
+++ b/pkgs/convert/test/string_replacer_test.dart
@@ -6,6 +6,17 @@
 import 'package:convert/convert.dart';
 import 'package:test/test.dart';
 
+// On dart2wasm, JSStringImpl.substring(0, length) allocates a new Wasm struct
+// wrapper around the JS externref rather than returning `this`.
+const _isWasm = bool.fromEnvironment('dart.tool.dart2wasm');
+
+void _expectSameString(String actual, String expected) {
+  expect(actual, expected);
+  if (!_isWasm) {
+    expect(identical(actual, expected), isTrue);
+  }
+}
+
 void main() {
   group('StringReplacer', () {
     test('rejects empty key in constructor and static replace', () {
@@ -28,8 +39,8 @@
     test('empty map returns identical input', () {
       final replacer = StringReplacer(const {});
       final input = String.fromCharCodes('hello'.codeUnits);
-      expect(identical(replacer.convert(input), input), isTrue);
-      expect(identical(StringReplacer.replace(input, const {}), input), isTrue);
+      _expectSameString(replacer.convert(input), input);
+      _expectSameString(StringReplacer.replace(input, const {}), input);
       expect(replacer.convert(input, 1, 4), 'ell');
       expect(StringReplacer.replace(input, const {}, 1, 4), 'ell');
     });
@@ -46,17 +57,17 @@
       final multiReplacer = StringReplacer({'az': 'Y', 'bc': 'Z'});
       final input = String.fromCharCodes('abd_efg'.codeUnits);
 
-      expect(identical(singleReplacer.convert(input), input), isTrue);
-      expect(
-        identical(StringReplacer.replace(input, {'x': 'y', 'z': 'w'}), input),
-        isTrue,
+      _expectSameString(singleReplacer.convert(input), input);
+      _expectSameString(
+        StringReplacer.replace(input, {'x': 'y', 'z': 'w'}),
+        input,
       );
       // Note: 'a' and 'b' appear in `input`, so the first-unit table bucket is
       // non-null, but neither 'az' nor 'bc' matches. Must still return `input`.
-      expect(identical(multiReplacer.convert(input), input), isTrue);
-      expect(
-        identical(StringReplacer.replace(input, {'az': 'Y', 'bc': 'Z'}), input),
-        isTrue,
+      _expectSameString(multiReplacer.convert(input), input);
+      _expectSameString(
+        StringReplacer.replace(input, {'az': 'Y', 'bc': 'Z'}),
+        input,
       );
     });
 
@@ -186,8 +197,8 @@
       final replacer = StringReplacer(map);
       final padding = '.' * 80;
       final noMatch = String.fromCharCodes('${padding}baz$padding'.codeUnits);
-      expect(identical(replacer.convert(noMatch), noMatch), isTrue);
-      expect(identical(StringReplacer.replace(noMatch, map), noMatch), isTrue);
+      _expectSameString(replacer.convert(noMatch), noMatch);
+      _expectSameString(StringReplacer.replace(noMatch, map), noMatch);
 
       final oneMatch = '${padding}foo$padding';
       final expectedOne = '${padding}bar$padding';
@@ -253,8 +264,8 @@
 
       // 1. 0 active keys in long string -> identical return
       final none = String.fromCharCodes('$pad.$pad.$pad'.codeUnits);
-      expect(identical(replacer.convert(none), none), isTrue);
-      expect(identical(StringReplacer.replace(none, map), none), isTrue);
+      _expectSameString(replacer.convert(none), none);
+      _expectSameString(StringReplacer.replace(none, map), none);
       expect(replacer.convert(none, 10, none.length - 10),
           none.substring(10, none.length - 10));
       expect(StringReplacer.replace(none, map, 10, none.length - 10),