Skip to content

Commit 55f2754

Browse files
osa1Commit Queue
authored andcommitted
[dart2wasm] Fix JSStringImpl trim methods, add tests
Fix trim methods returning a new string (instead of the argument) when there's nothing to trim. A second bug fixed in NEL handling in `trim`: the last line should be `result.substring(...)` instead of `this.substring(...)`. The bug wasn't caught by any of the tests: NEL needs to be handled specially on the browser, but mixing NEL with other whitespace wasn't tested in the existing tests `string_trim_test` and `string_trimlr_test`, so a new test file added with this case. Change-Id: Ibf84e185de1b26c9811e3ea58c2ad3f223da8515 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/363081 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Ömer Ağacan <[email protected]> Reviewed-by: Lasse Nielsen <[email protected]>
1 parent 166e465 commit 55f2754

File tree

2 files changed

+76
-18
lines changed

2 files changed

+76
-18
lines changed

sdk/lib/_internal/wasm/lib/js_string.dart

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ final class JSStringImpl implements String {
328328
String substring(int start, [int? end]) {
329329
end ??= length;
330330
RangeErrorUtils.checkValidRangePositiveLength(start, end, length);
331+
if (start == end) return "";
331332
return JSStringImpl(_jsSubstring(toExternRef, start, end));
332333
}
333334

@@ -441,73 +442,96 @@ final class JSStringImpl implements String {
441442
return index;
442443
}
443444

444-
// dart2wasm can't use JavaScript trim directly,
445-
// because JavaScript does not trim
446-
// the NEXT LINE (NEL) character (0x85).
445+
// dart2wasm can't use JavaScript trim directly, because JavaScript does not
446+
// trim the NEXT LINE (NEL) character (0x85).
447447
@override
448448
String trim() {
449-
// Start by doing JS trim. Then check if it leaves a NEL at
450-
// either end of the string.
449+
final length = this.length;
450+
if (length == 0) return this;
451+
452+
// Start by doing JS trim. Then check if it leaves a NEL at either end of
453+
// the string.
451454
final result =
452455
JSStringImpl(js.JS<WasmExternRef?>('s => s.trim()', toExternRef));
453456
final resultLength = result.length;
454457
if (resultLength == 0) return result;
455-
int firstCode = result._codeUnitAtUnchecked(0);
458+
459+
// Check NEL on the left.
460+
final int firstCode = result._codeUnitAtUnchecked(0);
456461
int startIndex = 0;
457462
if (firstCode == nelCodeUnit) {
458463
startIndex = _skipLeadingWhitespace(result, 1);
459464
if (startIndex == resultLength) return "";
460465
}
461466

467+
// Check NEL on the right.
462468
int endIndex = resultLength;
463469
// We know that there is at least one character that is non-whitespace.
464470
// Therefore we don't need to verify that endIndex > startIndex.
465-
int lastCode = result.codeUnitAt(endIndex - 1);
471+
final int lastCode = result.codeUnitAt(endIndex - 1);
466472
if (lastCode == nelCodeUnit) {
467473
endIndex = _skipTrailingWhitespace(result, endIndex - 1);
468474
}
469-
if (startIndex == 0 && endIndex == resultLength) return result;
470-
return substring(startIndex, endIndex);
475+
476+
if (startIndex == 0 && endIndex == resultLength) {
477+
return length == resultLength ? this : result;
478+
}
479+
480+
return result.substring(startIndex, endIndex);
471481
}
472482

473483
// dart2wasm can't use JavaScript trimLeft directly because it does not trim
474-
// the NEXT LINE character (0x85).
484+
// the NEXT LINE (NEL) character (0x85).
475485
@override
476486
String trimLeft() {
477-
// Start by doing JS trim. Then check if it leaves a NEL at
478-
// the beginning of the string.
487+
final length = this.length;
488+
if (length == 0) return this;
489+
490+
// Start by doing JS trim. Then check if it leaves a NEL at the beginning
491+
// of the string.
479492
int startIndex = 0;
480493
final result =
481494
JSStringImpl(js.JS<WasmExternRef?>('s => s.trimLeft()', toExternRef));
482495
final resultLength = result.length;
483496
if (resultLength == 0) return result;
497+
498+
// Check NEL.
484499
int firstCode = result._codeUnitAtUnchecked(0);
485500
if (firstCode == nelCodeUnit) {
486501
startIndex = _skipLeadingWhitespace(result, 1);
487502
}
488-
if (startIndex == 0) return result;
489-
if (startIndex == resultLength) return "";
503+
504+
if (startIndex == 0) {
505+
return resultLength == length ? this : result;
506+
}
507+
490508
return result.substring(startIndex);
491509
}
492510

493511
// dart2wasm can't use JavaScript trimRight directly because it does not trim
494-
// the NEXT LINE character (0x85).
512+
// the NEXT LINE (NEL) character (0x85).
495513
@override
496514
String trimRight() {
515+
final length = this.length;
516+
if (length == 0) return this;
517+
497518
// Start by doing JS trim. Then check if it leaves a NEL at the end of the
498519
// string.
499520
final result =
500521
JSStringImpl(js.JS<WasmExternRef?>('s => s.trimRight()', toExternRef));
501522
final resultLength = result.length;
523+
if (resultLength == 0) return result;
524+
502525
int endIndex = resultLength;
503-
if (endIndex == 0) return result;
504526
int lastCode = result.codeUnitAt(endIndex - 1);
505527
if (lastCode == nelCodeUnit) {
506528
endIndex = _skipTrailingWhitespace(result, endIndex - 1);
507529
}
508530

509-
if (endIndex == resultLength) return result;
510-
if (endIndex == 0) return "";
531+
if (endIndex == resultLength) {
532+
return resultLength == length ? this : result;
533+
}
534+
511535
return result.substring(0, endIndex);
512536
}
513537

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// NEXT LINE (NEL, 0x85) character is special in that some Unicode versions do
6+
// not include it as a whitespace character.
7+
//
8+
// However all Dart backends handle it as a whitespace character. dart2js and
9+
// dart2wasm do this by a calling JS (`String.prototype.trim` and its
10+
// left/right variants) and doing post-processing. To check these
11+
// implementations tests in this file mix the SPACE character with NEL in
12+
// various positions.
13+
//
14+
// string_trim_lr_test.dart tests various NEL characters on the left and right,
15+
// but they do not mix NEL with other whitespace characters.
16+
// string_trim_test.dart tests do not test NEL.
17+
18+
import "package:expect/expect.dart";
19+
20+
const int space = 0x20;
21+
const int nel = 0x85;
22+
const int h = 0x68;
23+
const int i = 0x69;
24+
25+
void main() {
26+
Expect.equals(
27+
"hi", String.fromCharCodes([space, nel, space, h, i]).trimLeft());
28+
Expect.equals(
29+
"hi", String.fromCharCodes([h, i, space, nel, space]).trimRight());
30+
Expect.equals(
31+
"hi",
32+
String.fromCharCodes([space, nel, space, h, i, space, nel, space])
33+
.trim());
34+
}

0 commit comments

Comments
 (0)