diff --git a/pkg/dart2wasm/lib/closures.dart b/pkg/dart2wasm/lib/closures.dart index 48b7baf08ac..61b1faf6872 100644 --- a/pkg/dart2wasm/lib/closures.dart +++ b/pkg/dart2wasm/lib/closures.dart @@ -704,19 +704,8 @@ class ClosureLayouter extends RecursiveVisitor { b.struct_get(contextStructType, typeFieldIdx); b.local_get(otherContextLocal); b.struct_get(contextStructType, typeFieldIdx); - - // Virtual call to `Object.==` - final selector = translator.dispatchTable - .selectorForTarget(translator.coreTypes.objectEquals.reference); - final selectorOffset = selector.offset!; - b.local_get(thisContextLocal); - b.struct_get(contextStructType, typeFieldIdx); - b.struct_get(translator.topInfo.struct, FieldIndex.classId); - if (selectorOffset != 0) { - b.i32_const(selectorOffset); - b.i32_add(); - } - b.call_indirect(selector.signature, translator.dispatchTable.wasmTable); + b.call(translator.functions + .getFunction(translator.runtimeTypeEquals.reference)); b.if_(); } diff --git a/pkg/dart2wasm/lib/kernel_nodes.dart b/pkg/dart2wasm/lib/kernel_nodes.dart index 53c6ff6a64f..1fa86aba064 100644 --- a/pkg/dart2wasm/lib/kernel_nodes.dart +++ b/pkg/dart2wasm/lib/kernel_nodes.dart @@ -215,6 +215,8 @@ mixin KernelNodes { index.getProcedure("dart:_string", "StringBase", "_interpolate"); late final Procedure truncDiv = index.getProcedure("dart:core", "_BoxedInt", "_truncDiv"); + late final Procedure runtimeTypeEquals = + index.getTopLevelProcedure("dart:core", "_runtimeTypeEquals"); // dart:core invocation/exception procedures late final Procedure invocationGetterFactory = diff --git a/sdk/lib/_internal/wasm/lib/type.dart b/sdk/lib/_internal/wasm/lib/type.dart index 4ba192e184c..282b096c94e 100644 --- a/sdk/lib/_internal/wasm/lib/type.dart +++ b/sdk/lib/_internal/wasm/lib/type.dart @@ -1405,3 +1405,11 @@ _Type _getMasqueradedRuntimeTypeNullable(Object? object) => external bool _isObjectClassId(int classId); external bool _isClosureClassId(int classId); external bool _isRecordClassId(int classId); + +// Used by the generated code to compare types captured by instantiation +// closures. Because we don't have a way of forcing adding a member the +// dispatch table (like the entry-point pragma) we can't generate a virtual +// call to `_Type.==` directly in the generated code. +@pragma("wasm:entry-point") +@pragma("wasm:prefer-inline") +bool _runtimeTypeEquals(_Type t1, _Type t2) => t1 == t2; diff --git a/tests/web/wasm/issue_54926_test.dart b/tests/web/wasm/issue_54926_test.dart new file mode 100644 index 00000000000..03b7df004a0 --- /dev/null +++ b/tests/web/wasm/issue_54926_test.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2024, 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. + +// Instantiation closure equality check used to generate virtual call to +// `Object.==` when comparing captured types. +// +// When the program is small, `Object.==` is sometimes not added to the +// dispatch table, which causes a crash. +// +// To avoid making the program larger (which can make `Object.==` available for +// virtual calls and hide the bug), this does not use the `expect` library. + +class C { + void f(T t) { + print(t); + } +} + +void main() { + var f = C().f; + var f1 = f; + var f2 = f; + + if (identical(f1, f2) || f1 != f2) { + throw ''; + } +}