diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart index 87e30c4b7d0..ff37a62a8bf 100644 --- a/pkg/dart2wasm/lib/target.dart +++ b/pkg/dart2wasm/lib/target.dart @@ -521,6 +521,13 @@ class WasmTarget extends Target { coreTypes.index.getClass('dart:_string', 'OneByteString'); } + // In dart2wasm we can't assume that `x == "hello"` means `x`'s class is + // `concreteStringLiteralClass("hello")`, it may also be `JSStringImpl` when + // it's obtained from a JS call, or `TwoByteString` when it's a substring of a + // `TwoByteString`. + @override + bool get canInferStringClassAfterEqualityComparison => false; + @override Class concreteClosureClass(CoreTypes coreTypes) { return _closure ??= coreTypes.index.getClass('dart:core', '_Closure'); diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart index fda4156a957..dcdda51e318 100644 --- a/pkg/kernel/lib/target/targets.dart +++ b/pkg/kernel/lib/target/targets.dart @@ -508,6 +508,10 @@ abstract class Target { Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null; Class? concreteStringLiteralClass(CoreTypes coreTypes, String value) => null; + /// When a comparison `x == ` is true, whether we can assume the + /// class of `x` to be `concreteStringLiteralClass()`. + bool get canInferStringClassAfterEqualityComparison => true; + Class? concreteAsyncResultClass(CoreTypes coreTypes) => null; Class? concreteSyncStarResultClass(CoreTypes coreTypes) => null; diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart index 19bdfccefc5..87cfc9f1afa 100644 --- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart +++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart @@ -1582,6 +1582,7 @@ class SummaryCollector extends RecursiveResultVisitor { _isSubtype(lhs.variable.type, _environment.coreTypes.intNullableRawType)) || (rhs is StringLiteral && + target.canInferStringClassAfterEqualityComparison && _isSubtype(lhs.variable.type, _environment.coreTypes.stringNullableRawType)) || (rhs is ConstantExpression && diff --git a/tests/web/wasm/issue_59901_test.dart b/tests/web/wasm/issue_59901_test.dart new file mode 100644 index 00000000000..04b15dfd8ab --- /dev/null +++ b/tests/web/wasm/issue_59901_test.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:js_interop'; + +import 'package:expect/expect.dart'; + +void main() { + final List codeUnits = []; + final jsString = "hello".toJS.toDart; + if (jsString == "hello") { + for (int i = 0; i < 5; i += 1) { + codeUnits.add(jsString.codeUnitAt(i)); + } + } + Expect.listEquals(codeUnits, [104, 101, 108, 108, 111]); +}