[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>
This commit is contained in:
Alexander Aprelev
2022-02-09 16:49:49 +00:00
committed by Commit Bot
parent f481cbf2fa
commit 6b830c4428
3 changed files with 90 additions and 51 deletions
+12 -51
View File
@@ -330,7 +330,6 @@ class 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,
@@ -415,7 +414,6 @@ static const char* NewConstChar(const char* chars) {
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 @@ IsolateSpawnState::IsolateSpawnState(Dart_Port parent_port,
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 @@ static const char* String2UTF8(const String& str) {
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 @@ DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 10) {
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 @@ DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 10) {
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()));
@@ -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;
}
}
@@ -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;
}
}