Version 2.15.0-38.0.dev
Merge commit '709f87e7f3ac9359eb857e60c5585d32d5266924' into 'dev'
diff --git a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
index d0bfa3e..5626076 100644
--- a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
@@ -188,8 +188,14 @@
.where((String arg) => !arg.contains('optimization-counter-threshold'))
.toList();
+ // We first compile the testee to kernel and run the subprocess on the kernel
+ // file. That avoids cases where the testee has to run a lot of code in the
+ // kernel-isolate (e.g. due to ia32's kernel-service not being app-jit
+ // trained). We do that because otherwise the --complete-timeline will collect
+ // a lot of data, possibly leading to OOMs or timeouts.
await runVMTests(args, tests,
testeeBefore: primeTimeline,
extraArgs: ['--complete-timeline'],
- executableArgs: executableArgs);
+ executableArgs: executableArgs,
+ compileToKernelFirst: true);
}
diff --git a/runtime/observatory/tests/service/test_helper.dart b/runtime/observatory/tests/service/test_helper.dart
index 0369d53..487c098 100644
--- a/runtime/observatory/tests/service/test_helper.dart
+++ b/runtime/observatory/tests/service/test_helper.dart
@@ -7,8 +7,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
+
import 'package:dds/dds.dart';
import 'package:observatory/service_io.dart';
+import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'service_test_common.dart';
export 'service_test_common.dart' show DDSTest, IsolateTest, VMTest;
@@ -94,7 +96,8 @@
final _processCompleter = Completer<void>();
bool killedByTester = false;
- _ServiceTesteeLauncher() : args = [Platform.script.toFilePath()] {}
+ _ServiceTesteeLauncher({String? script})
+ : args = [script ?? Platform.script.toFilePath()] {}
// Spawn the testee process.
Future<Process> _spawnProcess(
@@ -343,6 +346,7 @@
bool testeeControlsServer: false,
bool enableDds: true,
bool enableService: true,
+ bool compileToKernelFirst: false,
int port = 0,
}) {
if (executableArgs == null) {
@@ -352,6 +356,7 @@
late WebSocketVM vm;
late _ServiceTesteeLauncher process;
bool testsDone = false;
+ final tempDir = Directory.systemTemp.createTempSync('testee_dill');
ignoreLateException(Function f) async {
try {
@@ -369,7 +374,26 @@
setUp(
() => ignoreLateException(
() async {
- process = _ServiceTesteeLauncher();
+ String testeePath = Platform.script.toFilePath();
+ if (compileToKernelFirst && testeePath.endsWith('.dart')) {
+ final testeePathDill = path.join(tempDir.path, 'testee.dill');
+ final ProcessResult result =
+ await Process.run(Platform.executable, [
+ '--snapshot-kind=kernel',
+ '--snapshot=$testeePathDill',
+ ...Platform.executableArguments,
+ testeePath,
+ ]);
+ if (result.exitCode != 0) {
+ throw 'Failed to compile testee to kernel:\n'
+ 'stdout: ${result.stdout}\n'
+ 'stderr: ${result.stderr}\n'
+ 'exitCode: ${result.exitCode}\n';
+ }
+ testeePath = testeePathDill;
+ }
+
+ process = _ServiceTesteeLauncher(script: testeePath);
await process
.launch(
pause_on_start,
@@ -611,6 +635,7 @@
bool enable_service_port_fallback: false,
bool enableDds: true,
bool enableService: true,
+ bool compileToKernelFirst: false,
int port = 0,
List<String>? extraArgs,
List<String>? executableArgs}) async {
@@ -633,6 +658,7 @@
enable_service_port_fallback: enable_service_port_fallback,
enableDds: enableDds,
enableService: enableService,
+ compileToKernelFirst: compileToKernelFirst,
port: port,
);
}
diff --git a/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
index 94e2654..0f8e1de 100644
--- a/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
@@ -188,8 +188,14 @@
.where((String arg) => !arg.contains('optimization-counter-threshold'))
.toList();
+ // We first compile the testee to kernel and run the subprocess on the kernel
+ // file. That avoids cases where the testee has to run a lot of code in the
+ // kernel-isolate (e.g. due to ia32's kernel-service not being app-jit
+ // trained). We do that because otherwise the --complete-timeline will collect
+ // a lot of data, possibly leading to OOMs or timeouts.
await runVMTests(args, tests,
testeeBefore: primeTimeline,
extraArgs: ['--complete-timeline'],
- executableArgs: executableArgs);
+ executableArgs: executableArgs,
+ compileToKernelFirst: true);
}
diff --git a/runtime/observatory_2/tests/service_2/test_helper.dart b/runtime/observatory_2/tests/service_2/test_helper.dart
index 570e830..86e7b13 100644
--- a/runtime/observatory_2/tests/service_2/test_helper.dart
+++ b/runtime/observatory_2/tests/service_2/test_helper.dart
@@ -9,8 +9,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
+
import 'package:dds/dds.dart';
import 'package:observatory_2/service_io.dart';
+import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'service_test_common.dart';
export 'service_test_common.dart' show DDSTest, IsolateTest, VMTest;
@@ -96,7 +98,8 @@
final _processCompleter = Completer<void>();
bool killedByTester = false;
- _ServiceTesteeLauncher() : args = [Platform.script.toFilePath()] {}
+ _ServiceTesteeLauncher({String script})
+ : args = [script ?? Platform.script.toFilePath()] {}
// Spawn the testee process.
Future<Process> _spawnProcess(
@@ -341,6 +344,7 @@
bool testeeControlsServer: false,
bool enableDds: true,
bool enableService: true,
+ bool compileToKernelFirst: false,
int port = 0,
}) {
if (executableArgs == null) {
@@ -350,6 +354,7 @@
WebSocketVM vm;
_ServiceTesteeLauncher process;
bool testsDone = false;
+ final tempDir = Directory.systemTemp.createTempSync('testee_dill');
ignoreLateException(Function f) async {
try {
@@ -367,7 +372,26 @@
setUp(
() => ignoreLateException(
() async {
- process = _ServiceTesteeLauncher();
+ String testeePath = Platform.script.toFilePath();
+ if (compileToKernelFirst && testeePath.endsWith('.dart')) {
+ final testeePathDill = path.join(tempDir.path, 'testee.dill');
+ final ProcessResult result =
+ await Process.run(Platform.executable, [
+ '--snapshot-kind=kernel',
+ '--snapshot=$testeePathDill',
+ ...Platform.executableArguments,
+ testeePath,
+ ]);
+ if (result.exitCode != 0) {
+ throw 'Failed to compile testee to kernel:\n'
+ 'stdout: ${result.stdout}\n'
+ 'stderr: ${result.stderr}\n'
+ 'exitCode: ${result.exitCode}\n';
+ }
+ testeePath = testeePathDill;
+ }
+
+ process = _ServiceTesteeLauncher(script: testeePath);
await process
.launch(
pause_on_start,
@@ -411,6 +435,7 @@
await dds?.shutdown();
}
process.requestExit();
+ tempDir.deleteSync(recursive: true);
},
),
);
@@ -609,6 +634,7 @@
bool enable_service_port_fallback: false,
bool enableDds: true,
bool enableService: true,
+ bool compileToKernelFirst: false,
int port = 0,
List<String> extraArgs,
List<String> executableArgs}) async {
@@ -631,6 +657,7 @@
enable_service_port_fallback: enable_service_port_fallback,
enableDds: enableDds,
enableService: enableService,
+ compileToKernelFirst: compileToKernelFirst,
port: port,
);
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index d45a059..857404b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5497,6 +5497,12 @@
}
return true;
}
+
+ // _Closure <: Function
+ if (this_class.IsClosureClass() && other_class.IsDartFunctionClass()) {
+ return true;
+ }
+
// Check for 'direct super type' specified in the implements clause
// and check for transitivity at the same time.
Array& interfaces = Array::Handle(zone, this_class.interfaces());
@@ -20453,17 +20459,12 @@
return false;
}
// Function types cannot be handled by Class::IsSubtypeOf().
- if (other.IsDartFunctionType()) {
- // Any type that can be the type of a closure is a subtype of Function.
- if (IsDartFunctionType() || IsDartClosureType() || IsFunctionType()) {
- if (isolate_group->use_strict_null_safety_checks() && IsNullable()) {
- return !other.IsNonNullable();
- }
- return true;
- }
- // Fall through.
- }
if (IsFunctionType()) {
+ // Any type that can be the type of a closure is a subtype of Function.
+ if (other.IsDartFunctionType()) {
+ return !isolate_group->use_strict_null_safety_checks() || !IsNullable() ||
+ !other.IsNonNullable();
+ }
if (other.IsFunctionType()) {
// Check for two function types.
if (isolate_group->use_strict_null_safety_checks() && IsNullable() &&
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index fa73cff..959f4da 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -5305,13 +5305,108 @@
}
ISOLATE_UNIT_TEST_CASE(ClosureType_SubtypeOfFunctionType) {
+ auto check_subtype_relation = [](const Expect& expect, const Type& sub,
+ const Type& super, bool is_subtype) {
+ if (sub.IsSubtypeOf(super, Heap::kNew) != is_subtype) {
+ TextBuffer buffer(128);
+ buffer.AddString("Expected ");
+ sub.PrintName(Object::kScrubbedName, &buffer);
+ buffer.Printf(" to %s a subtype of ", is_subtype ? "be" : "not be");
+ super.PrintName(Object::kScrubbedName, &buffer);
+ expect.Fail("%s", buffer.buffer());
+ }
+ };
+#define EXPECT_SUBTYPE(sub, super) \
+ check_subtype_relation(Expect(__FILE__, __LINE__), sub, super, true);
+#define EXPECT_NOT_SUBTYPE(sub, super) \
+ check_subtype_relation(Expect(__FILE__, __LINE__), sub, super, false);
+
+ auto finalize_and_canonicalize = [](Type* type) {
+ *type ^= ClassFinalizer::FinalizeType(*type);
+ ASSERT(type->IsCanonical());
+ };
+
const auto& closure_class =
Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
const auto& closure_type = Type::Handle(closure_class.DeclarationType());
+ auto& closure_type_nullable = Type::Handle(
+ closure_type.ToNullability(Nullability::kNullable, Heap::kNew));
+ finalize_and_canonicalize(&closure_type_nullable);
+ auto& closure_type_legacy = Type::Handle(
+ closure_type.ToNullability(Nullability::kLegacy, Heap::kNew));
+ finalize_and_canonicalize(&closure_type_legacy);
+ auto& closure_type_nonnullable = Type::Handle(
+ closure_type.ToNullability(Nullability::kNonNullable, Heap::kNew));
+ finalize_and_canonicalize(&closure_type_nonnullable);
+
const auto& function_type =
Type::Handle(IsolateGroup::Current()->object_store()->function_type());
+ auto& function_type_nullable = Type::Handle(
+ function_type.ToNullability(Nullability::kNullable, Heap::kNew));
+ finalize_and_canonicalize(&function_type_nullable);
+ auto& function_type_legacy = Type::Handle(
+ function_type.ToNullability(Nullability::kLegacy, Heap::kNew));
+ finalize_and_canonicalize(&function_type_legacy);
+ auto& function_type_nonnullable = Type::Handle(
+ function_type.ToNullability(Nullability::kNonNullable, Heap::kNew));
+ finalize_and_canonicalize(&function_type_nonnullable);
- EXPECT(closure_type.IsSubtypeOf(function_type, Heap::kNew));
+ EXPECT_SUBTYPE(closure_type_nonnullable, function_type_nullable);
+ EXPECT_SUBTYPE(closure_type_nonnullable, function_type_legacy);
+ EXPECT_SUBTYPE(closure_type_nonnullable, function_type_nonnullable);
+ EXPECT_SUBTYPE(closure_type_legacy, function_type_nullable);
+ EXPECT_SUBTYPE(closure_type_legacy, function_type_legacy);
+ EXPECT_SUBTYPE(closure_type_legacy, function_type_nonnullable);
+ EXPECT_SUBTYPE(closure_type_nullable, function_type_nullable);
+ EXPECT_SUBTYPE(closure_type_nullable, function_type_legacy);
+ // Nullable types are not a subtype of non-nullable types in strict mode.
+ if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
+ EXPECT_NOT_SUBTYPE(closure_type_nullable, function_type_nonnullable);
+ } else {
+ EXPECT_SUBTYPE(closure_type_nullable, function_type_nonnullable);
+ }
+
+ const auto& async_lib = Library::Handle(Library::AsyncLibrary());
+ const auto& future_or_class =
+ Class::Handle(async_lib.LookupClass(Symbols::FutureOr()));
+ auto& tav_function_nullable = TypeArguments::Handle(TypeArguments::New(1));
+ tav_function_nullable.SetTypeAt(0, function_type_nullable);
+ tav_function_nullable = tav_function_nullable.Canonicalize(thread, nullptr);
+ auto& tav_function_legacy = TypeArguments::Handle(TypeArguments::New(1));
+ tav_function_legacy.SetTypeAt(0, function_type_legacy);
+ tav_function_legacy = tav_function_legacy.Canonicalize(thread, nullptr);
+ auto& tav_function_nonnullable = TypeArguments::Handle(TypeArguments::New(1));
+ tav_function_nonnullable.SetTypeAt(0, function_type_nonnullable);
+ tav_function_nonnullable =
+ tav_function_nonnullable.Canonicalize(thread, nullptr);
+
+ auto& future_or_function_type_nullable =
+ Type::Handle(Type::New(future_or_class, tav_function_nullable));
+ finalize_and_canonicalize(&future_or_function_type_nullable);
+ auto& future_or_function_type_legacy =
+ Type::Handle(Type::New(future_or_class, tav_function_legacy));
+ finalize_and_canonicalize(&future_or_function_type_legacy);
+ auto& future_or_function_type_nonnullable =
+ Type::Handle(Type::New(future_or_class, tav_function_nonnullable));
+ finalize_and_canonicalize(&future_or_function_type_nonnullable);
+
+ EXPECT_SUBTYPE(closure_type_nonnullable, future_or_function_type_nullable);
+ EXPECT_SUBTYPE(closure_type_nonnullable, future_or_function_type_legacy);
+ EXPECT_SUBTYPE(closure_type_nonnullable, future_or_function_type_nonnullable);
+ EXPECT_SUBTYPE(closure_type_legacy, future_or_function_type_nullable);
+ EXPECT_SUBTYPE(closure_type_legacy, future_or_function_type_legacy);
+ EXPECT_SUBTYPE(closure_type_legacy, future_or_function_type_nonnullable);
+ EXPECT_SUBTYPE(closure_type_nullable, future_or_function_type_nullable);
+ EXPECT_SUBTYPE(closure_type_nullable, future_or_function_type_legacy);
+ // Nullable types are not a subtype of non-nullable types in strict mode.
+ if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
+ EXPECT_NOT_SUBTYPE(closure_type_nullable,
+ future_or_function_type_nonnullable);
+ } else {
+ EXPECT_SUBTYPE(closure_type_nullable, future_or_function_type_nonnullable);
+ }
+#undef EXPECT_NOT_SUBTYPE
+#undef EXPECT_SUBTYPE
}
TEST_CASE(Class_GetInstantiationOf) {
diff --git a/tools/VERSION b/tools/VERSION
index 445bc0f..dfd5a96 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 37
+PRERELEASE 38
PRERELEASE_PATCH 0
\ No newline at end of file