diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart index 7d0f9a339a8..8bc7d8a6a6e 100644 --- a/pkg/dart2wasm/lib/code_generator.dart +++ b/pkg/dart2wasm/lib/code_generator.dart @@ -1818,13 +1818,19 @@ abstract class AstCodeGenerator w.ValueType visitDynamicInvocation( DynamicInvocation node, w.ValueType expectedType, + ) { + return _handleDynamicInvocation(node.receiver, node.arguments, node.name); + } + + w.ValueType _handleDynamicInvocation( + Expression receiver, + Arguments arguments, + Name memberName, ) { // Call dynamic invocation forwarder - final receiver = node.receiver; - final typeArguments = node.arguments.types; - final positionalArguments = node.arguments.positional; - final namedArguments = node.arguments.named; - final memberName = node.name; + final typeArguments = arguments.types; + final positionalArguments = arguments.positional; + final namedArguments = arguments.named; final callShape = MethodCallShape( memberName, typeArguments.length, @@ -2550,15 +2556,7 @@ abstract class AstCodeGenerator if (node.kind == FunctionAccessKind.Function) { // Type of function is `Function`, without the argument types. - return visitDynamicInvocation( - DynamicInvocation( - DynamicAccessKind.Dynamic, - node.receiver, - node.name, - node.arguments, - ), - expectedType, - ); + return _handleDynamicInvocation(node.receiver, node.arguments, node.name); } List argNames = node.arguments.named.map((a) => a.name).toList() diff --git a/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart b/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart new file mode 100644 index 00000000000..697860196ad --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart @@ -0,0 +1,32 @@ +// 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. + +import 'package:meta/meta.dart'; + +class EntityId { + final int id; + + const EntityId(this.id); +} + +EntityId get1(T t) { + return EntityId(1); +} + +EntityId get2(T t) { + return EntityId(2); +} + +const _idExtractors = {'foo': get1, 'bar': get2}; + +typedef _IdExtractor = EntityId Function(T thing); + +@RecordUse() +_IdExtractor idExtractor(String entityType) { + return (T v) => _idExtractors[entityType]!(v); +} + +void main() { + print(idExtractor('foo')); +} diff --git a/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart.aot.expect new file mode 100644 index 00000000000..c656b05291c --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart.aot.expect @@ -0,0 +1,43 @@ +library #lib; +import self as self; +import "dart:core" as core; +import "package:meta/meta.dart" as meta; + +import "package:meta/meta.dart"; + +class EntityId extends core::Object /*hasConstConstructor*/ { + const constructor •() → self::EntityId + : super core::Object::•() + ; +} + +[@vm.inferred-return-type.metadata=#lib::EntityId] +static method get1(self::get1::T% t) → self::EntityId { + return new self::EntityId::•(); +} + +[@vm.inferred-return-type.metadata=#lib::EntityId] +static method get2(self::get2::T% t) → self::EntityId { + return new self::EntityId::•(); +} + +[@vm.inferred-return-type.metadata=dart.core::_Closure (closure 1 in #lib::idExtractor)] +[@vm.closure-id=1] +[@vm.unboxing-info.metadata=[!regcc]] +@#C1 +static method idExtractor([@vm.inferred-type.metadata=dart.core::_OneByteString (value: "foo")] [@vm.inferred-arg-type.metadata=dart.core::_OneByteString (value: "foo")] core::String entityType) → (self::idExtractor::T%) → self::EntityId { + return [@vm.closure-id=1](self::idExtractor::T% v) → self::EntityId => [@vm.inferred-type.metadata=? (receiver not int)] [@vm.direct-call.metadata=dart._compact_hash::__ConstMap&_LinkedHashImmutableBase&MapMixin&_HashBase&_OperatorEqualsAndCanonicalHashCode&_LinkedHashMapMixin&_UnmodifiableMapMixin&_ImmutableLinkedHashMapMixin.[]] [@vm.inferred-type.metadata=dart.core::_Closure? (skip check)] #C6.{core::Map::[]}(entityType){(core::Object?) → core::Function?}!(v) as{TypeError,ForDynamic} self::EntityId; +} + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method main() → void { + core::print([@vm.inferred-type.metadata=dart.core::_Closure (closure 1 in #lib::idExtractor)] self::idExtractor("foo")); +} +constants { + #C1 = meta::RecordUse {} + #C2 = "foo" + #C3 = static-tearoff self::get1 + #C4 = "bar" + #C5 = static-tearoff self::get2 + #C6 = {#C2:#C3, #C4:#C5} +} diff --git a/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart.json.expect b/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart.json.expect new file mode 100644 index 00000000000..78213f58cbe --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/lib/function_invocation.dart.json.expect @@ -0,0 +1,43 @@ +{ + "constants": [ + { + "type": "string", + "value": "foo" + } + ], + "definitions": [ + { + "path": [ + { + "disambiguators": [ + "static" + ], + "kind": "method", + "name": "idExtractor" + } + ], + "uri": "package:record_use_test/function_invocation.dart" + } + ], + "loading_units": [ + { + "name": "1" + } + ], + "uses": { + "static_calls": [ + { + "definition_index": 0, + "uses": [ + { + "loading_unit_index": 0, + "positional": [ + 0 + ], + "type": "with_arguments" + } + ] + } + ] + } +} \ No newline at end of file