[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;
+  }
+}