diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart index 93c8d118a35..1e146d5980f 100644 --- a/pkg/dart2wasm/lib/dispatch_table.dart +++ b/pkg/dart2wasm/lib/dispatch_table.dart @@ -7,6 +7,7 @@ import 'dart:math' show min; import 'package:kernel/ast.dart'; import 'package:vm/metadata/procedure_attributes.dart'; import 'package:vm/metadata/table_selector.dart'; +import 'package:vm/metadata/unreachable.dart'; import 'package:wasm_builder/wasm_builder.dart' as w; import 'class_info.dart'; @@ -131,7 +132,11 @@ class SelectorInfo { final references = source.readList(source.readReference); final paramInfo = _parameterInfoFromReferences( - references, useSentinelForOptionalParameters); + source.component + .metadata[UnreachableNodeMetadataRepository.repositoryTag] + as UnreachableNodeMetadataRepository, + references, + useSentinelForOptionalParameters); return SelectorInfo._(dispatchTable, id, name, callCount, isSetter: isSetter) ..useMultipleEntryPoints = useMultipleEntryPoints @@ -747,7 +752,9 @@ class DispatchTable { selector.useMultipleEntryPoints = false; selector._useSentinelForOptionalParameters = true; selector.paramInfo = _parameterInfoFromReferences( - selector._references, selector._useSentinelForOptionalParameters); + translator.unreachableMetadata, + selector._references, + selector._useSentinelForOptionalParameters); } else { // Will be initialized in the `selectorTargets.forEach()` below. } @@ -814,7 +821,9 @@ class DispatchTable { selector._useSentinelForOptionalParameters = true; } selector.paramInfo = _parameterInfoFromReferences( - selector._references, selector._useSentinelForOptionalParameters); + translator.unreachableMetadata, + selector._references, + selector._useSentinelForOptionalParameters); // Split up [ranges] into those that are statically dispatched to and // those are used via dispatch table. @@ -1076,15 +1085,26 @@ bool _isUsedViaDispatchTableCall(SelectorInfo selector) { } ParameterInfo _parameterInfoFromReferences( - List references, bool useDefaultValueSentinel) { + UnreachableNodeMetadataRepository unreachableMetadata, + List references, + bool useDefaultValueSentinel) { + final unreachableNodeMapping = unreachableMetadata.mapping; // We know all target implementations (closed world) if all of them use // the same default value for optionals, we can make the caller pass it. final first = references.first; + final firstMember = first.asMember; final paramInfo = ParameterInfo.fromMember( - first, useDefaultValueSentinel || first.asMember.isAbstract); + first, + useDefaultValueSentinel || + firstMember.isAbstract || + unreachableNodeMapping[firstMember] != null); for (final target in references.skip(1)) { + final targetMember = target.asMember; paramInfo.merge(ParameterInfo.fromMember( - target, useDefaultValueSentinel || target.asMember.isAbstract)); + target, + useDefaultValueSentinel || + targetMember.isAbstract || + unreachableNodeMapping[targetMember] != null)); } return paramInfo; } diff --git a/pkg/dart2wasm/lib/serialization.dart b/pkg/dart2wasm/lib/serialization.dart index 035e22843b7..035cd84ae7d 100644 --- a/pkg/dart2wasm/lib/serialization.dart +++ b/pkg/dart2wasm/lib/serialization.dart @@ -178,11 +178,12 @@ class DataSerializer { } class DataDeserializer { + final Component component; final _BinaryDataSource _source; final _IdToEntityMapper _mapper; final List _stringIndexer = []; - DataDeserializer(Uint8List bytes, Component component) + DataDeserializer(Uint8List bytes, this.component) : _source = _BinaryDataSource(bytes), _mapper = _IdToEntityMapper(component); diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index 50aeb5cbd72..0aae3847d9b 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -15,6 +15,7 @@ import 'package:vm/metadata/direct_call.dart'; import 'package:vm/metadata/inferred_type.dart'; import 'package:vm/metadata/procedure_attributes.dart'; import 'package:vm/metadata/unboxing_info.dart'; +import 'package:vm/metadata/unreachable.dart'; import 'package:wasm_builder/wasm_builder.dart' as w; import 'class_info.dart'; @@ -184,6 +185,9 @@ class Translator with KernelNodes { (component.metadata[ProcedureAttributesMetadataRepository.repositoryTag] as ProcedureAttributesMetadataRepository) .mapping; + late final UnreachableNodeMetadataRepository unreachableMetadata = + component.metadata[UnreachableNodeMetadataRepository.repositoryTag] + as UnreachableNodeMetadataRepository; // Other parts of the global compiler state. @override diff --git a/pkg/dart2wasm/lib/util.dart b/pkg/dart2wasm/lib/util.dart index cab1859e3cf..555ab6a1984 100644 --- a/pkg/dart2wasm/lib/util.dart +++ b/pkg/dart2wasm/lib/util.dart @@ -16,6 +16,7 @@ import 'package:vm/metadata/procedure_attributes.dart' show ProcedureAttributesMetadataRepository; import 'package:vm/metadata/table_selector.dart' show TableSelectorMetadataRepository; +import 'package:vm/metadata/unreachable.dart'; final bool compilerAssertsEnabled = (() { bool compilerAsserts = false; @@ -104,6 +105,7 @@ String intToMinString(int i) { Component createEmptyComponent() { return Component() + ..addMetadataRepository(UnreachableNodeMetadataRepository()) ..addMetadataRepository(ProcedureAttributesMetadataRepository()) ..addMetadataRepository(TableSelectorMetadataRepository()) ..addMetadataRepository(DirectCallMetadataRepository()) diff --git a/pkg/vm/lib/metadata/unreachable.dart b/pkg/vm/lib/metadata/unreachable.dart index a4c44e05121..aafcda11e0f 100644 --- a/pkg/vm/lib/metadata/unreachable.dart +++ b/pkg/vm/lib/metadata/unreachable.dart @@ -19,8 +19,10 @@ class UnreachableNode { /// Repository for [UnreachableNode]. class UnreachableNodeMetadataRepository extends MetadataRepository { + static const repositoryTag = 'vm.unreachable.metadata'; + @override - final String tag = 'vm.unreachable.metadata'; + final String tag = repositoryTag; @override final Map mapping = {}; diff --git a/tests/web/wasm/regress_62273_test.dart b/tests/web/wasm/regress_62273_test.dart new file mode 100644 index 00000000000..5b2868c5db4 --- /dev/null +++ b/tests/web/wasm/regress_62273_test.dart @@ -0,0 +1,33 @@ +// Copyright (c) 2026, 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. + +class A { + void foo({String s = 'hi'}) { + print(s); + } +} + +class B extends A { + @override + void foo({String s = 'there'}) { + print(s); + } +} + +A getA(int x) { + if (x < 10) { + return B(); + } else { + // A is instantiated but the type cannot flow into a 'foo' call so A.foo's + // body is not reachable. + throw 'here'; + return A(); + } +} + +void main() { + getA(9).foo(); + // Ensure the default value is not inlined into the function. + getA(8).foo(s: 'wow'); +}