Skip to content

Commit

Permalink
[dart2wasm] Fix dynamic switch casts.
Browse files Browse the repository at this point in the history
If the switch's expression has type 'dynamic' and all the case expressions have the same type, we compare them using '=='. This requires a cast to ensure all the types match for the dispatch to the '==' function. However, we don't check that the type of the switch expression matches the type of the case expressions. So the cast fails if they don't match.

This adds a guard to ensure the types match before running through the case expressions. If the guard fails, we either jump to the default case or if there isn't one, we skip the switch entirely.

Fixes: #59782
Change-Id: I12e81f98d1c2046ee47e8ca4371642fd40620636
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402460
Commit-Queue: Nate Biggs <[email protected]>
Reviewed-by: Martin Kustermann <[email protected]>
  • Loading branch information
natebiggs authored and Commit Queue committed Jan 7, 2025
1 parent a70ab61 commit d80fab4
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 0 deletions.
24 changes: 24 additions & 0 deletions pkg/dart2wasm/lib/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,14 @@ abstract class AstCodeGenerator
b.local_set(switchValueNonNullableLocal);
}

final dynamicTypeGuard = switchInfo.dynamicTypeGuard;
if (dynamicTypeGuard != null) {
final success = b.block(const [], [translator.topInfo.nonNullableType]);
dynamicTypeGuard(switchValueNonNullableLocal, success);
b.br(switchLabels[defaultCase] ?? doneLabel);
b.end();
}

// Compare against all case values
for (SwitchCase c in node.cases) {
for (Expression exp in c.expressions) {
Expand Down Expand Up @@ -4114,6 +4122,12 @@ class SwitchInfo {
/// expression is nullable.
late final w.ValueType nonNullableType;

/// Generates code that will br on [successLabel] if [switchExprLocal] has the
/// correct type for the case checks on this switch. Only set for switches
/// where the switch expression is dynamic.
void Function(w.Local switchExprLocal, w.Label successLabel)?
dynamicTypeGuard;

/// Generates code that compares value of a `case` expression with the
/// `switch` expression's value. Calls [pushCaseExpr] once.
late final void Function(
Expand Down Expand Up @@ -4179,6 +4193,16 @@ class SwitchInfo {
// add missing optional parameters.
assert(equalsMemberSignature.inputs.length == 2);

dynamicTypeGuard = (switchExprLocal, successLabel) {
codeGen.b.local_get(switchExprLocal);
codeGen.b.br_on_cast(
successLabel,
switchExprLocal.type as w.RefType,
equalsMemberSignature.inputs[0]
.withNullability(switchExprLocal.type.nullable) as w.RefType);
codeGen.b.drop();
};

compare = (switchExprLocal, pushCaseExpr) {
final caseExprType = pushCaseExpr();
translator.convertType(
Expand Down
29 changes: 29 additions & 0 deletions tests/web/wasm/regress_59782_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 'package:expect/expect.dart';

void main() {
String? result;
dynamic number = 42;
switch (number) {
case 'a':
result = 'a';
case 'b':
result = 'b';
default:
result = 'default';
}

Expect.equals(result, 'default');

result = null;
switch (number) {
case 'a':
result = 'a';
case 'b':
result = 'b';
}
Expect.isNull(result);
}

0 comments on commit d80fab4

Please sign in to comment.