Skip to content

Commit 78ce85a

Browse files
osa1Commit Queue
authored andcommitted
[dart2wasm,tfa] Don't infer class of string values in comparisons
In dart2wasm, when a comparison like `x == "hello"` is true, we can't assume that the class of `x` is the same as the class of `"hello"`: - If `x` is received from JS, it will be `JSStringImpl`. - If it's a substring of a `TwoByteString`, it will be `TwoByteString`. - Otherwise it will be `OneByteString`. Update `Target` with the new method ``` bool get canInferStringClassAfterEqualityComparison => true; ``` to allow TFA to *not* infer classes of string values after comparisons. Override the method to return `false` in dart2wasm's `Target` implementation. Fixes #59901. Tested: web/wasm/issue_59901_test Change-Id: I1a6c8deaf27c54240dd4e821dbd8160914502ad7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404562 Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Ömer Ağacan <[email protected]>
1 parent b59c3e2 commit 78ce85a

File tree

4 files changed

+30
-0
lines changed

4 files changed

+30
-0
lines changed

pkg/dart2wasm/lib/target.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,13 @@ class WasmTarget extends Target {
521521
coreTypes.index.getClass('dart:_string', 'OneByteString');
522522
}
523523

524+
// In dart2wasm we can't assume that `x == "hello"` means `x`'s class is
525+
// `concreteStringLiteralClass("hello")`, it may also be `JSStringImpl` when
526+
// it's obtained from a JS call, or `TwoByteString` when it's a substring of a
527+
// `TwoByteString`.
528+
@override
529+
bool get canInferStringClassAfterEqualityComparison => false;
530+
524531
@override
525532
Class concreteClosureClass(CoreTypes coreTypes) {
526533
return _closure ??= coreTypes.index.getClass('dart:core', '_Closure');

pkg/kernel/lib/target/targets.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,10 @@ abstract class Target {
508508
Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null;
509509
Class? concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;
510510

511+
/// When a comparison `x == <literal>` is true, whether we can assume the
512+
/// class of `x` to be `concreteStringLiteralClass(<literal>)`.
513+
bool get canInferStringClassAfterEqualityComparison => true;
514+
511515
Class? concreteAsyncResultClass(CoreTypes coreTypes) => null;
512516
Class? concreteSyncStarResultClass(CoreTypes coreTypes) => null;
513517

pkg/vm/lib/transformations/type_flow/summary_collector.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,7 @@ class SummaryCollector extends RecursiveResultVisitor<TypeExpr?> {
15821582
_isSubtype(lhs.variable.type,
15831583
_environment.coreTypes.intNullableRawType)) ||
15841584
(rhs is StringLiteral &&
1585+
target.canInferStringClassAfterEqualityComparison &&
15851586
_isSubtype(lhs.variable.type,
15861587
_environment.coreTypes.stringNullableRawType)) ||
15871588
(rhs is ConstantExpression &&
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2025, 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+
import 'dart:js_interop';
6+
7+
import 'package:expect/expect.dart';
8+
9+
void main() {
10+
final List<int> codeUnits = [];
11+
final jsString = "hello".toJS.toDart;
12+
if (jsString == "hello") {
13+
for (int i = 0; i < 5; i += 1) {
14+
codeUnits.add(jsString.codeUnitAt(i));
15+
}
16+
}
17+
Expect.listEquals(codeUnits, [104, 101, 108, 108, 111]);
18+
}

0 commit comments

Comments
 (0)