[vm/isolate/spawn] Ensure spawnFunction sends closure so type args are preserved.
Fixes https://github.com/dart-lang/sdk/issues/48035
TEST=spawn_generic_function_test
Change-Id: I28d0ea9123bd31cb1aa288824c3c7688fc2ca8f1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/232160
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index f57a6c4..370b9d7 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -330,7 +330,6 @@
IsolateSpawnState(Dart_Port parent_port,
Dart_Port origin_id,
const char* script_url,
- const Function& func,
PersistentHandle* closure_tuple_handle,
SerializedObjectBuffer* message_buffer,
const char* package_config,
@@ -415,7 +414,6 @@
IsolateSpawnState::IsolateSpawnState(Dart_Port parent_port,
Dart_Port origin_id,
const char* script_url,
- const Function& func,
PersistentHandle* closure_tuple_handle,
SerializedObjectBuffer* message_buffer,
const char* package_config,
@@ -438,30 +436,11 @@
serialized_message_(message_buffer->StealMessage()),
paused_(paused),
errors_are_fatal_(errors_are_fatal) {
- // Either we have a top-level function or we have a closure.
- ASSERT((closure_tuple_handle_ != nullptr) == func.IsNull());
+ ASSERT(closure_tuple_handle_ != nullptr);
auto thread = Thread::Current();
auto isolate = thread->isolate();
- if (!func.IsNull()) {
- auto zone = thread->zone();
- const auto& cls = Class::Handle(zone, func.Owner());
- const auto& lib = Library::Handle(zone, cls.library());
- const auto& lib_url = String::Handle(zone, lib.url());
- library_url_ = NewConstChar(lib_url.ToCString());
-
- String& func_name = String::Handle(zone);
- func_name = func.name();
- function_name_ = NewConstChar(String::ScrubName(func_name));
- if (!cls.IsTopLevel()) {
- const auto& class_name = String::Handle(zone, cls.Name());
- class_name_ = NewConstChar(class_name.ToCString());
- }
- } else {
- ASSERT(closure_tuple_handle != nullptr);
- }
-
// Inherit flags from spawning isolate.
isolate->FlagsCopyTo(isolate_flags());
}
@@ -908,19 +887,6 @@
return result;
}
-static FunctionPtr GetTopLevelFunction(Zone* zone, const Instance& closure) {
- if (closure.IsClosure()) {
- auto& func = Function::Handle(zone);
- func = Closure::Cast(closure).function();
- if (func.IsImplicitClosureFunction() && func.is_static()) {
- ASSERT(Closure::Cast(closure).context() == Context::null());
- // Get the parent function so that we get the right function name.
- return func.parent_function();
- }
- }
- return Function::null();
-}
-
DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 10) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(String, script_uri, arguments->NativeArgAt(1));
@@ -933,20 +899,17 @@
GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(8));
GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(9));
- const auto& func = Function::Handle(zone, GetTopLevelFunction(zone, closure));
PersistentHandle* closure_tuple_handle = nullptr;
- if (func.IsNull()) {
- // We have a non-toplevel closure that we might need to copy.
- // Result will be [<closure-copy>, <objects-in-msg-to-rehash>]
- const auto& closure_copy_tuple = Object::Handle(
- zone, CopyMutableObjectGraph(closure)); // Throws if it fails.
- ASSERT(closure_copy_tuple.IsArray());
- ASSERT(Object::Handle(zone, Array::Cast(closure_copy_tuple).At(0))
- .IsClosure());
- closure_tuple_handle =
- isolate->group()->api_state()->AllocatePersistentHandle();
- closure_tuple_handle->set_ptr(closure_copy_tuple.ptr());
- }
+ // We have a non-toplevel closure that we might need to copy.
+ // Result will be [<closure-copy>, <objects-in-msg-to-rehash>]
+ const auto& closure_copy_tuple = Object::Handle(
+ zone, CopyMutableObjectGraph(closure)); // Throws if it fails.
+ ASSERT(closure_copy_tuple.IsArray());
+ ASSERT(
+ Object::Handle(zone, Array::Cast(closure_copy_tuple).At(0)).IsClosure());
+ closure_tuple_handle =
+ isolate->group()->api_state()->AllocatePersistentHandle();
+ closure_tuple_handle->set_ptr(closure_copy_tuple.ptr());
bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
@@ -964,15 +927,13 @@
const char* utf8_debug_name =
debugName.IsNull() ? NULL : String2UTF8(debugName);
if (closure_tuple_handle != nullptr && utf8_debug_name == nullptr) {
- ASSERT(func.IsNull());
-
const auto& closure_function = Function::Handle(zone, closure.function());
utf8_debug_name =
NewConstChar(closure_function.QualifiedUserVisibleNameCString());
}
std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
- port.Id(), isolate->origin_id(), String2UTF8(script_uri), func,
+ port.Id(), isolate->origin_id(), String2UTF8(script_uri),
closure_tuple_handle, &message_buffer, utf8_package_config,
paused.value(), fatal_errors, on_exit_port, on_error_port,
utf8_debug_name, isolate->group()));
diff --git a/runtime/tests/vm/dart/isolates/spawn_generic_function_test.dart b/runtime/tests/vm/dart/isolates/spawn_generic_function_test.dart
new file mode 100644
index 0000000..3b9831a
--- /dev/null
+++ b/runtime/tests/vm/dart/isolates/spawn_generic_function_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, 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.
+//
+// Tests that generic functions are spawned with correct type arguments
+
+import 'dart:isolate';
+import 'package:expect/expect.dart';
+
+void func<T>(T o) {
+ print("$o:$T");
+ Expect.equals("int", "$T");
+}
+
+void call2(dynamic f) {
+ f(2);
+}
+
+void call4(dynamic f) {
+ f(4);
+}
+
+void main() async {
+ {
+ final rp = ReceivePort();
+ Isolate.spawn(func<int>, 1, onExit: rp.sendPort);
+ await rp.first;
+ }
+ {
+ final rp = ReceivePort();
+ Isolate.spawn(call2, func<int>, onExit: rp.sendPort);
+ await rp.first;
+ }
+ void Function(int) to = func;
+ {
+ final rp = ReceivePort();
+ Isolate.spawn(to, 3, onExit: rp.sendPort);
+ await rp.first;
+ }
+ {
+ final rp = ReceivePort();
+ Isolate.spawn(call4, to, onExit: rp.sendPort);
+ await rp.first;
+ }
+}
diff --git a/runtime/tests/vm/dart_2/isolates/spawn_generic_function_test.dart b/runtime/tests/vm/dart_2/isolates/spawn_generic_function_test.dart
new file mode 100644
index 0000000..8529015
--- /dev/null
+++ b/runtime/tests/vm/dart_2/isolates/spawn_generic_function_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, 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.
+//
+// Tests that generic functions are spawned with correct type arguments
+
+// @dart = 2.9
+
+import 'dart:isolate';
+import 'package:expect/expect.dart';
+
+void func<T>(T o) {
+ print("$o:$T");
+ Expect.equals("int", "$T");
+}
+
+void call4(dynamic f) {
+ f(4);
+}
+
+void main() async {
+ void Function(int) to = func;
+ {
+ final rp = ReceivePort();
+ Isolate.spawn(to, 3, onExit: rp.sendPort);
+ await rp.first;
+ }
+ {
+ final rp = ReceivePort();
+ Isolate.spawn(call4, to, onExit: rp.sendPort);
+ await rp.first;
+ }
+}