[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:
committed by
Commit Bot
parent
f481cbf2fa
commit
6b830c4428
+12
-51
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user