[dart2wasm] Implement some missing members on String.
Change-Id: I37db0a4fa2ebdd0101832e0e4025cb5a38434dc1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251720
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/dart2wasm/bin/run_wasm.js b/pkg/dart2wasm/bin/run_wasm.js
index fbd9196..8d376f8 100644
--- a/pkg/dart2wasm/bin/run_wasm.js
+++ b/pkg/dart2wasm/bin/run_wasm.js
@@ -36,7 +36,7 @@
} else {
var dartString = dartInstance.exports.$stringAllocate2(length);
for (var i = 0; i < length; i++) {
- dartInstance.exports.$stringWrite2(dartString, i, string.codePointAt(i));
+ dartInstance.exports.$stringWrite2(dartString, i, string.charCodeAt(i));
}
return dartString;
}
@@ -252,6 +252,12 @@
jsonEncode: function(s) {
return stringToDartString(JSON.stringify(stringFromDartString(s)));
},
+ toUpperCase: function(string) {
+ return stringToDartString(stringFromDartString(string).toUpperCase());
+ },
+ toLowerCase: function(string) {
+ return stringToDartString(stringFromDartString(string).toLowerCase());
+ },
};
function instantiate(filename, imports) {
diff --git a/sdk/lib/_internal/wasm/lib/string_patch.dart b/sdk/lib/_internal/wasm/lib/string_patch.dart
index b5e57fd..e39db23 100644
--- a/sdk/lib/_internal/wasm/lib/string_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/string_patch.dart
@@ -13,6 +13,12 @@
const int _maxUtf16 = 0xffff;
const int _maxUnicode = 0x10ffff;
+@pragma("wasm:import", "dart2wasm.toUpperCase")
+external String _toUpperCase(String string);
+
+@pragma("wasm:import", "dart2wasm.toLowerCase")
+external String _toLowerCase(String string);
+
@patch
class String {
@patch
@@ -31,15 +37,12 @@
return _TwoByteString._allocate(1).._setAt(0, charCode);
}
if (charCode <= 0x10ffff) {
- var low = 0xDC00 | (charCode & 0x3ff);
+ int low = 0xDC00 | (charCode & 0x3ff);
int bits = charCode - 0x10000;
- var high = 0xD800 | (bits >> 10);
- return _StringBase._createFromCodePoints(
- new _List(2)
- ..[0] = high
- ..[1] = low,
- 0,
- 2);
+ int high = 0xD800 | (bits >> 10);
+ return _TwoByteString._allocate(2)
+ .._setAt(0, high)
+ .._setAt(1, low);
}
}
throw new RangeError.range(charCode, 0, 0x10ffff);
@@ -240,8 +243,34 @@
return s;
}
- external static String _createFromCodePoints(
- List<int> codePoints, int start, int end);
+ static String _createFromOneByteCodes(
+ List<int> charCodes, int start, int end) {
+ _OneByteString result = _OneByteString._allocate(end - start);
+ for (int i = start; i < end; i++) {
+ result._setAt(i - start, charCodes[i]);
+ }
+ return result;
+ }
+
+ static String _createFromCodePoints(List<int> charCodes, int start, int end) {
+ for (int i = start; i < end; i++) {
+ int c = charCodes[i];
+ if (c < 0) throw ArgumentError.value(i);
+ if (c > 0xff) {
+ return _createFromAdjustedCodePoints(charCodes, start, end);
+ }
+ }
+ return _createFromOneByteCodes(charCodes, start, end);
+ }
+
+ static String _createFromAdjustedCodePoints(
+ List<int> codePoints, int start, int end) {
+ StringBuffer a = StringBuffer();
+ for (int i = start; i < end; i++) {
+ a.writeCharCode(codePoints[i]);
+ }
+ return a.toString();
+ }
String operator [](int index) => String.fromCharCode(codeUnitAt(index));
@@ -685,8 +714,82 @@
* whether the result must be a one-byte string.
*/
- external static String _joinReplaceAllResult(
- String base, List matches, int length, bool replacementStringsAreOneByte);
+ String _joinReplaceAllResult(String base, List matches, int length,
+ bool replacementStringsAreOneByte) {
+ if (length < 0) throw ArgumentError.value(length);
+ bool isOneByte = replacementStringsAreOneByte &&
+ _slicesAreOneByte(base, matches, length);
+ if (isOneByte) {
+ return _joinReplaceAllOneByteResult(base, matches, length);
+ }
+ _TwoByteString result = _TwoByteString._allocate(length);
+ int writeIndex = 0;
+ for (int i = 0; i < matches.length; i++) {
+ var entry = matches[i];
+ if (entry is _Smi) {
+ int sliceStart = entry;
+ int sliceEnd;
+ if (sliceStart < 0) {
+ int bits = -sliceStart;
+ int sliceLength = bits & _lengthMask;
+ sliceStart = bits >> _lengthBits;
+ sliceEnd = sliceStart + sliceLength;
+ } else {
+ i++;
+ // This function should only be called with valid matches lists.
+ // If the list is short, or sliceEnd is not an integer, one of
+ // the next few lines will throw anyway.
+ assert(i < matches.length);
+ sliceEnd = matches[i];
+ }
+ for (int j = sliceStart; j < sliceEnd; j++) {
+ result._setAt(writeIndex++, base.codeUnitAt(j));
+ }
+ } else {
+ // Replacement is a one-byte string.
+ String replacement = entry;
+ for (int j = 0; j < replacement.length; j++) {
+ result._setAt(writeIndex++, replacement.codeUnitAt(j));
+ }
+ }
+ }
+ assert(writeIndex == length);
+ return result;
+ }
+
+ bool _slicesAreOneByte(String base, List matches, int length) {
+ for (int i = 0; i < matches.length; i++) {
+ Object? o = matches[i];
+ if (o is int) {
+ int sliceStart = o;
+ int sliceEnd;
+ if (sliceStart < 0) {
+ int bits = -sliceStart;
+ int sliceLength = bits & _lengthMask;
+ sliceStart = bits >> _lengthBits;
+ sliceEnd = sliceStart + sliceLength;
+ } else {
+ i++;
+ if (i >= length) {
+ // Invalid, handled later.
+ return false;
+ }
+ Object? p = matches[i];
+ if (p is! int) {
+ // Invalid, handled later.
+ return false;
+ }
+ sliceEnd = p;
+ }
+ for (int j = sliceStart; j < sliceEnd; j++) {
+ if (base.codeUnitAt(j) > 0xff) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
String replaceAllMapped(Pattern pattern, String replace(Match match)) {
List matches = [];
@@ -875,9 +978,9 @@
Runes get runes => new Runes(this);
- external String toUpperCase();
+ String toUpperCase() => _toUpperCase(this);
- external String toLowerCase();
+ String toLowerCase() => _toLowerCase(this);
// Concatenate ['start', 'end'[ elements of 'strings'.
static String _concatRange(List<String> strings, int start, int end) {