Version 2.13.0-154.0.dev
Merge commit 'dd9b00f7b93d72f8e174bfc630f02617726d9307' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index beda8cf..1b1635e 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2021-03-18T10:46:33.455990",
+ "generated": "2021-03-19T12:24:33.467679",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@@ -244,7 +244,7 @@
"name": "dds",
"rootUri": "../pkg/dds",
"packageUri": "lib/",
- "languageVersion": "2.6"
+ "languageVersion": "2.12"
},
{
"name": "dev_compiler",
@@ -581,7 +581,7 @@
"name": "shelf_proxy",
"rootUri": "../third_party/pkg/shelf_proxy",
"packageUri": "lib/",
- "languageVersion": "2.0"
+ "languageVersion": "2.12"
},
{
"name": "shelf_static",
@@ -791,7 +791,7 @@
"name": "webdriver",
"rootUri": "../third_party/pkg/webdriver",
"packageUri": "lib/",
- "languageVersion": "2.0"
+ "languageVersion": "2.12"
},
{
"name": "webkit_inspection_protocol",
diff --git a/DEPS b/DEPS
index f148696..225447d 100644
--- a/DEPS
+++ b/DEPS
@@ -112,7 +112,7 @@
"http_multi_server_rev" : "6bf4b6e5d4d890e6d54559b858ff229d79711171",
"http_parser_rev": "5dd4d16693242049dfb43b5efa429fedbf932e98",
"http_retry_rev": "845771af7bb5ab38ab740ce4a31f3b0c7680302b",
- "http_rev": "d5c678cd63c3e9c1d779a09acfa95b7e3af84665",
+ "http_rev": "615380db687d0649057ed8dba9d933d7d5f5051b",
"http_throttle_tag" : "1.0.2",
"icu_rev" : "81d656878ec611cb0b42d52c82e9dae93920d9ba",
"idl_parser_rev": "5fb1ebf49d235b5a70c9f49047e83b0654031eb7",
@@ -141,11 +141,11 @@
"resource_rev": "6b79867d0becf5395e5819a75720963b8298e9a7",
"root_certificates_rev": "7e5ec82c99677a2e5b95ce296c4d68b0d3378ed8",
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
- "shelf_static_rev": "bafde9eaddb5d02040a614e41deddd971b4d67e6",
+ "shelf_static_rev": "fa30419055279a00c9e428439b1abe362d18f25d",
"shelf_packages_handler_rev": "78302e67c035047e6348e692b0c1182131f0fe35",
- "shelf_proxy_tag": "0.1.0+7",
- "shelf_rev": "e9294125f0c1fc2049c958577cd586ca2395168f",
- "shelf_web_socket_rev": "aa312d3cdeef96fb64bc3e602bc354a05a995624",
+ "shelf_proxy_tag": "v1.0.0",
+ "shelf_rev": "00e50adfb776602c25942a99d89f8704cc20db9c",
+ "shelf_web_socket_rev": "24fb8a04befa75a94ac63a27047b231d1a22aab4",
"source_map_stack_trace_rev": "1c3026f69d9771acf2f8c176a1ab750463309cce",
"source_maps-0.9.4_rev": "38524",
"source_maps_rev": "53eb92ccfe6e64924054f83038a534b959b12b3e",
@@ -165,9 +165,9 @@
"usage_rev": "6c64d9e7b6b3758d06d030efcb5afe20bfc04dde",
"vector_math_rev": "0c9f5d68c047813a6dcdeb88ba7a42daddf25025",
"watcher_rev": "3924194385fb215cef483193ed2879a618a3d69c",
- "webdriver_rev": "5a8d6805d9cf8a3cbb4fcd64849b538b7491e50e",
+ "webdriver_rev": "ff5ccb1522edf4bed578ead4d65e0cbc1f2c4f02",
"web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
- "web_socket_channel_rev": "76931ea1b81ba71e8319330c35285d3e88566315",
+ "web_socket_channel_rev": "d3e100de8feb0283a04732366bb591ebd2282d7d",
"WebCore_rev": "fb11e887f77919450e497344da570d780e078bc8",
"webkit_inspection_protocol_rev": "6b15729292d030f2e5c5861022da4c5a4c11961c",
"yaml_rev": "b4c4411631bda556ce9a45af1ab0eecaf9f3ac53",
diff --git a/WATCHLISTS b/WATCHLISTS
index 23100a3..e4b5b89 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -18,6 +18,9 @@
'^tools/buildtools/'
)
},
+ 'experimental_features': {
+ 'filepath': 'tools/experimental_features\\.yaml',
+ },
'front_end': {
'filepath': '^pkg/front_end',
},
@@ -75,6 +78,7 @@
'WATCHLISTS': {
'dart2js': [ 'dart2js-team+reviews@google.com' ],
'dartdevc': [ 'dart-dc-team+reviews@google.com' ],
+ 'experimental_features': [ 'scheglov@google.com' ],
'front_end': [ 'dart-fe-team+reviews@google.com' ],
'kernel': [ 'jensj@google.com', 'alexmarkov@google.com' ],
'messages_review': [ 'dart-uxr+reviews@google.com' ],
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index 62904a1..e374a78 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 1.7.6
+- Update dependencies.
+
# 1.7.5
- Add 30 second keep alive period for SSE connections.
diff --git a/pkg/dds/bin/dds.dart b/pkg/dds/bin/dds.dart
index 3ac9a09..9937d26 100644
--- a/pkg/dds/bin/dds.dart
+++ b/pkg/dds/bin/dds.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:io';
import 'package:dds/dds.dart';
diff --git a/pkg/dds/example/example.dart b/pkg/dds/example/example.dart
index 883c678..561dca9 100644
--- a/pkg/dds/example/example.dart
+++ b/pkg/dds/example/example.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'package:dds/dds.dart';
import 'package:vm_service/vm_service_io.dart';
diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart
index a9024dd..f7c7a05 100644
--- a/pkg/dds/lib/dds.dart
+++ b/pkg/dds/lib/dds.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
/// A library used to spawn the Dart Developer Service, used to communicate
/// with a Dart VM Service instance.
library dds;
diff --git a/pkg/dds/lib/src/binary_compatible_peer.dart b/pkg/dds/lib/src/binary_compatible_peer.dart
index b701f5e..2e1af84 100644
--- a/pkg/dds/lib/src/binary_compatible_peer.dart
+++ b/pkg/dds/lib/src/binary_compatible_peer.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
diff --git a/pkg/dds/lib/src/client.dart b/pkg/dds/lib/src/client.dart
index 0c854c3..f81bd25 100644
--- a/pkg/dds/lib/src/client.dart
+++ b/pkg/dds/lib/src/client.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
diff --git a/pkg/dds/lib/src/client_manager.dart b/pkg/dds/lib/src/client_manager.dart
index b168978..c911c51 100644
--- a/pkg/dds/lib/src/client_manager.dart
+++ b/pkg/dds/lib/src/client_manager.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'client.dart';
diff --git a/pkg/dds/lib/src/constants.dart b/pkg/dds/lib/src/constants.dart
index c6450d5..2466390 100644
--- a/pkg/dds/lib/src/constants.dart
+++ b/pkg/dds/lib/src/constants.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
abstract class RPCResponses {
static const success = <String, dynamic>{
'type': 'Success',
diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart
index 6daaedd..db355fb 100644
--- a/pkg/dds/lib/src/dds_impl.dart
+++ b/pkg/dds/lib/src/dds_impl.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'dart:convert';
import 'dart:io';
diff --git a/pkg/dds/lib/src/expression_evaluator.dart b/pkg/dds/lib/src/expression_evaluator.dart
index bb69bb5..59bbe36 100644
--- a/pkg/dds/lib/src/expression_evaluator.dart
+++ b/pkg/dds/lib/src/expression_evaluator.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'client.dart';
diff --git a/pkg/dds/lib/src/isolate_manager.dart b/pkg/dds/lib/src/isolate_manager.dart
index 2714021..a5139d5 100644
--- a/pkg/dds/lib/src/isolate_manager.dart
+++ b/pkg/dds/lib/src/isolate_manager.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'client.dart';
diff --git a/pkg/dds/lib/src/logging_repository.dart b/pkg/dds/lib/src/logging_repository.dart
index f87fdaa..0b31ca6 100644
--- a/pkg/dds/lib/src/logging_repository.dart
+++ b/pkg/dds/lib/src/logging_repository.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:math';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
diff --git a/pkg/dds/lib/src/named_lookup.dart b/pkg/dds/lib/src/named_lookup.dart
index 603bbc0..8515411 100644
--- a/pkg/dds/lib/src/named_lookup.dart
+++ b/pkg/dds/lib/src/named_lookup.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
// Originally pulled from dart:_vmservice.
import 'dart:collection';
diff --git a/pkg/dds/lib/src/rpc_error_codes.dart b/pkg/dds/lib/src/rpc_error_codes.dart
index 93c514e..895c07c 100644
--- a/pkg/dds/lib/src/rpc_error_codes.dart
+++ b/pkg/dds/lib/src/rpc_error_codes.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
abstract class RpcErrorCodes {
diff --git a/pkg/dds/lib/src/stream_manager.dart b/pkg/dds/lib/src/stream_manager.dart
index 28c7f28..26bc6d7 100644
--- a/pkg/dds/lib/src/stream_manager.dart
+++ b/pkg/dds/lib/src/stream_manager.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:typed_data';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
diff --git a/pkg/dds/lib/vm_service_extensions.dart b/pkg/dds/lib/vm_service_extensions.dart
index 8373b87..0b6aaf4 100644
--- a/pkg/dds/lib/vm_service_extensions.dart
+++ b/pkg/dds/lib/vm_service_extensions.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'dart:collection';
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index 469fc0e..221e3df 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -3,27 +3,27 @@
A library used to spawn the Dart Developer Service, used to communicate with
a Dart VM Service instance.
-version: 1.7.5
+version: 1.7.6
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
environment:
- sdk: '>=2.6.0 <3.0.0'
+ sdk: '>=2.12.0 <3.0.0'
dependencies:
async: ^2.4.1
json_rpc_2: ^2.2.0
meta: ^1.1.8
pedantic: ^1.7.0
- shelf: ^0.7.5
- shelf_proxy: ^0.1.0+7
- shelf_web_socket: ^0.2.3
+ shelf: ^1.0.0
+ shelf_proxy: ^1.0.0
+ shelf_web_socket: ^1.0.0
sse: ^3.7.0
stream_channel: ^2.0.0
vm_service: ^6.0.1-nullsafety.0
- web_socket_channel: ^1.1.0
+ web_socket_channel: ^2.0.0
dev_dependencies:
- shelf_static: ^0.2.8
+ shelf_static: ^1.0.0
test: ^1.0.0
- webdriver: ^2.1.2
+ webdriver: ^3.0.0
diff --git a/pkg/dds/test/auth_codes_test.dart b/pkg/dds/test/auth_codes_test.dart
index 653ff41..fa8cd44 100644
--- a/pkg/dds/test/auth_codes_test.dart
+++ b/pkg/dds/test/auth_codes_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:convert';
import 'dart:io';
diff --git a/pkg/dds/test/common/fakes.dart b/pkg/dds/test/common/fakes.dart
index 7e11f9d..663cdd0 100644
--- a/pkg/dds/test/common/fakes.dart
+++ b/pkg/dds/test/common/fakes.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
diff --git a/pkg/dds/test/common/test_helper.dart b/pkg/dds/test/common/test_helper.dart
index cda9ce2..7cdb204 100644
--- a/pkg/dds/test/common/test_helper.dart
+++ b/pkg/dds/test/common/test_helper.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:convert';
import 'dart:io';
diff --git a/pkg/dds/test/external_compilation_service_script.dart b/pkg/dds/test/external_compilation_service_script.dart
index b01ed60..00c9208 100644
--- a/pkg/dds/test/external_compilation_service_script.dart
+++ b/pkg/dds/test/external_compilation_service_script.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:developer';
main() {
diff --git a/pkg/dds/test/external_compilation_service_test.dart b/pkg/dds/test/external_compilation_service_test.dart
index 92e5ac8..f31b9c0 100644
--- a/pkg/dds/test/external_compilation_service_test.dart
+++ b/pkg/dds/test/external_compilation_service_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:io';
import 'package:dds/dds.dart';
diff --git a/pkg/dds/test/get_stream_history_script.dart b/pkg/dds/test/get_stream_history_script.dart
index 68dafec..1f8b820 100644
--- a/pkg/dds/test/get_stream_history_script.dart
+++ b/pkg/dds/test/get_stream_history_script.dart
@@ -1,3 +1,5 @@
+// @dart = 2.10
+
import 'dart:developer';
void main() {
diff --git a/pkg/dds/test/get_stream_history_test.dart b/pkg/dds/test/get_stream_history_test.dart
index c2ae8f9..6631d24 100644
--- a/pkg/dds/test/get_stream_history_test.dart
+++ b/pkg/dds/test/get_stream_history_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:io';
import 'package:dds/dds.dart';
diff --git a/pkg/dds/test/handles_client_disconnect_state_error_test.dart b/pkg/dds/test/handles_client_disconnect_state_error_test.dart
index 87d027c..974814a 100644
--- a/pkg/dds/test/handles_client_disconnect_state_error_test.dart
+++ b/pkg/dds/test/handles_client_disconnect_state_error_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'package:dds/dds.dart';
diff --git a/pkg/dds/test/handles_connection_closed_before_full_header.dart b/pkg/dds/test/handles_connection_closed_before_full_header.dart
index 80f1744..7be1043 100644
--- a/pkg/dds/test/handles_connection_closed_before_full_header.dart
+++ b/pkg/dds/test/handles_connection_closed_before_full_header.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'dart:io';
diff --git a/pkg/dds/test/on_event_with_history_script.dart b/pkg/dds/test/on_event_with_history_script.dart
index 3f27df0..e35decc 100644
--- a/pkg/dds/test/on_event_with_history_script.dart
+++ b/pkg/dds/test/on_event_with_history_script.dart
@@ -1,3 +1,5 @@
+// @dart=2.10
+
import 'dart:developer';
void main() {
diff --git a/pkg/dds/test/on_event_with_history_test.dart b/pkg/dds/test/on_event_with_history_test.dart
index 55e04b9..42f25a1 100644
--- a/pkg/dds/test/on_event_with_history_test.dart
+++ b/pkg/dds/test/on_event_with_history_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'dart:io';
diff --git a/pkg/dds/test/smoke.dart b/pkg/dds/test/smoke.dart
index f4c4774..f1f92a2 100644
--- a/pkg/dds/test/smoke.dart
+++ b/pkg/dds/test/smoke.dart
@@ -1,3 +1,5 @@
+// @dart=2.10
+
void main() {
print('Hello world!');
}
diff --git a/pkg/dds/test/smoke_test.dart b/pkg/dds/test/smoke_test.dart
index 983a2da..2f59ef0 100644
--- a/pkg/dds/test/smoke_test.dart
+++ b/pkg/dds/test/smoke_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:convert';
import 'dart:io';
diff --git a/pkg/dds/test/sse_smoke_test.dart b/pkg/dds/test/sse_smoke_test.dart
index 9d4a9a6..2b5e696 100644
--- a/pkg/dds/test/sse_smoke_test.dart
+++ b/pkg/dds/test/sse_smoke_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:async';
import 'dart:convert';
import 'dart:io';
diff --git a/pkg/dds/test/uri_test.dart b/pkg/dds/test/uri_test.dart
index 8c7e64e..dad20f3 100644
--- a/pkg/dds/test/uri_test.dart
+++ b/pkg/dds/test/uri_test.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
import 'dart:io';
import 'package:dds/dds.dart';
diff --git a/pkg/dds/test/web/sse_smoke_driver.dart b/pkg/dds/test/web/sse_smoke_driver.dart
index 4f0ab25..f8769f5 100644
--- a/pkg/dds/test/web/sse_smoke_driver.dart
+++ b/pkg/dds/test/web/sse_smoke_driver.dart
@@ -2,6 +2,8 @@
// 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.
+// @dart=2.10
+
// This file must be compiled for changes to be picked up.
//
// Run the following command from the root of this package if this file is
diff --git a/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart b/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart
index b884f68..ba03221 100644
--- a/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart
@@ -12,11 +12,10 @@
import '../fasta_codes.dart'
show
- templateCycleInTypeVariables,
templateInternalProblemUnfinishedTypeVariable,
templateTypeArgumentsOnTypeVariable;
-import '../source/source_library_builder.dart' show SourceLibraryBuilder;
+import '../source/source_library_builder.dart';
import '../util/helpers.dart';
import 'class_builder.dart';
@@ -40,8 +39,8 @@
final bool isExtensionTypeParameter;
- TypeVariableBuilder(
- String name, SourceLibraryBuilder compilationUnit, int charOffset,
+ TypeVariableBuilder(String name, SourceLibraryBuilder compilationUnit,
+ int charOffset, Uri fileUri,
{this.bound,
this.isExtensionTypeParameter: false,
int variableVariance,
@@ -49,7 +48,7 @@
: actualParameter = new TypeParameter(name, null)
..fileOffset = charOffset
..variance = variableVariance,
- super(metadata, 0, name, compilationUnit, charOffset);
+ super(metadata, 0, name, compilationUnit, charOffset, fileUri);
TypeVariableBuilder.fromKernel(
TypeParameter parameter, LibraryBuilder compilationUnit)
@@ -116,12 +115,12 @@
DartType type = buildTypesWithBuiltArguments(library, nullability, null);
if (needsPostUpdate) {
if (library is SourceLibraryBuilder) {
- library.pendingNullabilities.add(type);
+ library.registerPendingNullability(fileUri, charOffset, type);
} else {
library.addProblem(
templateInternalProblemUnfinishedTypeVariable.withArguments(
name, library?.importUri),
- parameter.fileOffset,
+ charOffset,
name.length,
fileUri);
}
@@ -166,74 +165,6 @@
: dynamicType.build(library));
}
- /// Assigns nullabilities to types in [pendingNullabilities].
- ///
- /// It's a helper function to assign the nullabilities to type-parameter types
- /// after the corresponding type parameters have their bounds set or changed.
- /// The function takes into account that some of the types in the input list
- /// may be bounds to some of the type parameters of other types from the input
- /// list.
- static void finishNullabilities(LibraryBuilder libraryBuilder,
- List<TypeParameterType> pendingNullabilities) {
- // The bounds of type parameters may be type-parameter types of other
- // parameters from the same declaration. In this case we need to set the
- // nullability for them first. To preserve the ordering, we implement a
- // depth-first search over the types. We use the fact that a nullability
- // of a type parameter type can't ever be 'nullable' if computed from the
- // bound. It allows us to use 'nullable' nullability as the marker in the
- // DFS implementation.
- Nullability marker = Nullability.nullable;
- List<TypeParameterType> stack =
- new List<TypeParameterType>.filled(pendingNullabilities.length, null);
- int stackTop = 0;
- for (TypeParameterType type in pendingNullabilities) {
- type.declaredNullability = null;
- }
- for (TypeParameterType type in pendingNullabilities) {
- if (type.declaredNullability != null) {
- // Nullability for [type] was already computed on one of the branches
- // of the depth-first search. Continue to the next one.
- continue;
- }
- if (type.parameter.bound is TypeParameterType) {
- TypeParameterType current = type;
- TypeParameterType next = current.parameter.bound;
- while (next != null && next.declaredNullability == null) {
- stack[stackTop++] = current;
- current.declaredNullability = marker;
-
- current = next;
- if (current.parameter.bound is TypeParameterType) {
- next = current.parameter.bound;
- if (next.declaredNullability == marker) {
- next.declaredNullability = Nullability.undetermined;
- libraryBuilder.addProblem(
- templateCycleInTypeVariables.withArguments(
- next.parameter.name, current.parameter.name),
- next.parameter.fileOffset,
- next.parameter.name.length,
- next.parameter.location.file);
- next = null;
- }
- } else {
- next = null;
- }
- }
- current.declaredNullability =
- TypeParameterType.computeNullabilityFromBound(current.parameter);
- while (stackTop != 0) {
- --stackTop;
- current = stack[stackTop];
- current.declaredNullability =
- TypeParameterType.computeNullabilityFromBound(current.parameter);
- }
- } else {
- type.declaredNullability =
- TypeParameterType.computeNullabilityFromBound(type.parameter);
- }
- }
- }
-
void applyPatch(covariant TypeVariableBuilder patch) {
patch.actualOrigin = this;
}
@@ -242,7 +173,7 @@
// TODO(dmitryas): Figure out if using [charOffset] here is a good idea.
// An alternative is to use the offset of the node the cloned type variable
// is declared on.
- return new TypeVariableBuilder(name, parent, charOffset,
+ return new TypeVariableBuilder(name, parent, charOffset, fileUri,
bound: bound.clone(newTypes), variableVariance: variance);
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 4386b20..c697bac 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -5881,8 +5881,8 @@
debugEvent("beginTypeVariable");
Identifier name = pop();
List<Expression> annotations = pop();
- TypeVariableBuilder variable =
- new TypeVariableBuilder(name.name, libraryBuilder, name.charOffset);
+ TypeVariableBuilder variable = new TypeVariableBuilder(
+ name.name, libraryBuilder, name.charOffset, uri);
if (annotations != null) {
inferAnnotations(variable.parameter, annotations);
for (Expression annotation in annotations) {
@@ -5952,9 +5952,7 @@
libraryBuilder.loader.target.objectClassBuilder,
libraryBuilder.loader.target.dynamicType);
}
- TypeVariableBuilder.finishNullabilities(
- libraryBuilder, libraryBuilder.pendingNullabilities);
- libraryBuilder.pendingNullabilities.clear();
+ libraryBuilder.processPendingNullabilities();
}
@override
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 1a39e6a..ff30dfe 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -1169,7 +1169,10 @@
} else {
bool sentinelInserted = false;
if (nodeCache.containsKey(node)) {
- if (nodeCache[node] == null) {
+ bool isRecursiveFunctionCall =
+ node is MethodInvocation || node is StaticInvocation;
+ if (nodeCache[node] == null &&
+ !(enableConstFunctions && isRecursiveFunctionCall)) {
// recursive call
return createErrorConstant(node, messageConstEvalCircularity);
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
index 57f8b57..7aebe35 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
@@ -299,8 +299,8 @@
variance: Variance.invariant);
if (bound != variable.bound) {
TypeVariableBuilder newTypeVariableBuilder = variables[i] =
- new TypeVariableBuilder(
- variable.name, variable.parent, variable.charOffset,
+ new TypeVariableBuilder(variable.name, variable.parent,
+ variable.charOffset, variable.fileUri,
bound: bound);
unboundTypeVariables.add(newTypeVariableBuilder);
if (functionTypeUpperSubstitution == null) {
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 4fd5278..e9004a2 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -1843,7 +1843,8 @@
if (name is ParserRecovery) {
push(name);
} else {
- push(libraryBuilder.addTypeVariable(metadata, name, null, charOffset));
+ push(libraryBuilder.addTypeVariable(
+ metadata, name, null, charOffset, uri));
}
}
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index ce5fd71..6a3e269 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -203,7 +203,7 @@
// default nullability of the corresponding type-parameter types. This list
// is used to collect such type-parameter types in order to set the
// nullability after the bounds are built.
- final List<TypeParameterType> pendingNullabilities = <TypeParameterType>[];
+ final List<PendingNullability> _pendingNullabilities = <PendingNullability>[];
// A library to use for Names generated when compiling code in this library.
// This allows code generated in one library to use the private namespace of
@@ -2567,9 +2567,9 @@
}
TypeVariableBuilder addTypeVariable(List<MetadataBuilder> metadata,
- String name, TypeBuilder bound, int charOffset) {
+ String name, TypeBuilder bound, int charOffset, Uri fileUri) {
TypeVariableBuilder builder = new TypeVariableBuilder(
- name, this, charOffset,
+ name, this, charOffset, fileUri,
bound: bound, metadata: metadata);
boundlessTypeVariables.add(builder);
return builder;
@@ -2878,7 +2878,7 @@
List<TypeVariableBuilder> copy = <TypeVariableBuilder>[];
for (TypeVariableBuilder variable in original) {
TypeVariableBuilder newVariable = new TypeVariableBuilder(
- variable.name, this, variable.charOffset,
+ variable.name, this, variable.charOffset, variable.fileUri,
bound: variable.bound?.clone(newTypes),
isExtensionTypeParameter: isExtensionTypeParameter,
variableVariance:
@@ -2902,12 +2902,82 @@
}
boundlessTypeVariables.clear();
- TypeVariableBuilder.finishNullabilities(this, pendingNullabilities);
- pendingNullabilities.clear();
+ processPendingNullabilities();
return count;
}
+ /// Assigns nullabilities to types in [_pendingNullabilities].
+ ///
+ /// It's a helper function to assign the nullabilities to type-parameter types
+ /// after the corresponding type parameters have their bounds set or changed.
+ /// The function takes into account that some of the types in the input list
+ /// may be bounds to some of the type parameters of other types from the input
+ /// list.
+ void processPendingNullabilities() {
+ // The bounds of type parameters may be type-parameter types of other
+ // parameters from the same declaration. In this case we need to set the
+ // nullability for them first. To preserve the ordering, we implement a
+ // depth-first search over the types. We use the fact that a nullability
+ // of a type parameter type can't ever be 'nullable' if computed from the
+ // bound. It allows us to use 'nullable' nullability as the marker in the
+ // DFS implementation.
+ Nullability marker = Nullability.nullable;
+ List<TypeParameterType> stack =
+ new List<TypeParameterType>.filled(_pendingNullabilities.length, null);
+ int stackTop = 0;
+ for (PendingNullability pendingNullability in _pendingNullabilities) {
+ pendingNullability.type.declaredNullability = null;
+ }
+ for (PendingNullability pendingNullability in _pendingNullabilities) {
+ TypeParameterType type = pendingNullability.type;
+ if (type.declaredNullability != null) {
+ // Nullability for [type] was already computed on one of the branches
+ // of the depth-first search. Continue to the next one.
+ continue;
+ }
+ if (type.parameter.bound is TypeParameterType) {
+ TypeParameterType current = type;
+ TypeParameterType next = current.parameter.bound;
+ while (next != null && next.declaredNullability == null) {
+ stack[stackTop++] = current;
+ current.declaredNullability = marker;
+
+ current = next;
+ if (current.parameter.bound is TypeParameterType) {
+ next = current.parameter.bound;
+ if (next.declaredNullability == marker) {
+ next.declaredNullability = Nullability.undetermined;
+ current.parameter.bound = const InvalidType();
+ current.parameter.defaultType = const InvalidType();
+ addProblem(
+ templateCycleInTypeVariables.withArguments(
+ next.parameter.name, current.parameter.name),
+ pendingNullability.charOffset,
+ next.parameter.name.length,
+ pendingNullability.fileUri);
+ next = null;
+ }
+ } else {
+ next = null;
+ }
+ }
+ current.declaredNullability =
+ TypeParameterType.computeNullabilityFromBound(current.parameter);
+ while (stackTop != 0) {
+ --stackTop;
+ current = stack[stackTop];
+ current.declaredNullability =
+ TypeParameterType.computeNullabilityFromBound(current.parameter);
+ }
+ } else {
+ type.declaredNullability =
+ TypeParameterType.computeNullabilityFromBound(type.parameter);
+ }
+ }
+ _pendingNullabilities.clear();
+ }
+
int computeVariances() {
int count = 0;
for (Builder declaration in libraryDeclaration.members.values) {
@@ -3750,6 +3820,12 @@
referencesFromIndexed.lookupIndexedClass(name);
}
}
+
+ void registerPendingNullability(
+ Uri fileUri, int charOffset, TypeParameterType type) {
+ _pendingNullabilities
+ .add(new PendingNullability(fileUri, charOffset, type));
+ }
}
// The kind of type parameter scope built by a [TypeParameterScopeBuilder]
@@ -4208,3 +4284,11 @@
}
}
}
+
+class PendingNullability {
+ final Uri fileUri;
+ final int charOffset;
+ final TypeParameterType type;
+
+ PendingNullability(this.fileUri, this.charOffset, this.type);
+}
diff --git a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
index 6fb5965..1d8e254 100644
--- a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart
@@ -195,7 +195,8 @@
// otherwise it's a compiled library loaded from a dill file, and the
// bounds should have been assigned.
SourceLibraryBuilder parentLibrary = parent;
- parentLibrary.pendingNullabilities.add(asTypeArguments[i]);
+ parentLibrary.registerPendingNullability(_typeVariables[i].fileUri,
+ _typeVariables[i].charOffset, asTypeArguments[i]);
}
}
return result;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart
new file mode 100644
index 0000000..73f16fa
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, 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 recursive function calls for const functions.
+
+import "package:expect/expect.dart";
+
+const b = fn(4);
+int fn(int a) {
+ if (a == 1) return 1;
+ return a * fn(a - 1);
+}
+
+int localTest() {
+ int fnLocal(int a) {
+ if (a == 1) return 1;
+ return a * fnLocal(a - 1);
+ }
+
+ const c = fnLocal(4);
+ return c;
+}
+
+void main() {
+ Expect.equals(b, 24);
+ Expect.equals(localTest(), 24);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.strong.expect
new file mode 100644
index 0000000..c53d3dc
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.strong.expect
@@ -0,0 +1,29 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int b = #C1;
+static method fn(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(self::fn(a.{core::num::-}(1)));
+}
+static method localTest() → core::int {
+ function fnLocal(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(fnLocal.call(a.{core::num::-}(1)));
+ }
+ return #C1;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 24);
+ exp::Expect::equals(self::localTest(), 24);
+}
+
+constants {
+ #C1 = 24
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.strong.transformed.expect
new file mode 100644
index 0000000..c53d3dc
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.strong.transformed.expect
@@ -0,0 +1,29 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int b = #C1;
+static method fn(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(self::fn(a.{core::num::-}(1)));
+}
+static method localTest() → core::int {
+ function fnLocal(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(fnLocal.call(a.{core::num::-}(1)));
+ }
+ return #C1;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 24);
+ exp::Expect::equals(self::localTest(), 24);
+}
+
+constants {
+ #C1 = 24
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.textual_outline.expect
new file mode 100644
index 0000000..e73f951
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.textual_outline.expect
@@ -0,0 +1,6 @@
+import "package:expect/expect.dart";
+
+const b = fn(4);
+int fn(int a) {}
+int localTest() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..e73f951
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.textual_outline_modelled.expect
@@ -0,0 +1,6 @@
+import "package:expect/expect.dart";
+
+const b = fn(4);
+int fn(int a) {}
+int localTest() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.expect
new file mode 100644
index 0000000..c53d3dc
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.expect
@@ -0,0 +1,29 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int b = #C1;
+static method fn(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(self::fn(a.{core::num::-}(1)));
+}
+static method localTest() → core::int {
+ function fnLocal(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(fnLocal.call(a.{core::num::-}(1)));
+ }
+ return #C1;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 24);
+ exp::Expect::equals(self::localTest(), 24);
+}
+
+constants {
+ #C1 = 24
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.outline.expect
new file mode 100644
index 0000000..e25beae
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.outline.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int b = self::fn(4);
+static method fn(core::int a) → core::int
+ ;
+static method localTest() → core::int
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.transformed.expect
new file mode 100644
index 0000000..c53d3dc
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_recursion.dart.weak.transformed.expect
@@ -0,0 +1,29 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int b = #C1;
+static method fn(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(self::fn(a.{core::num::-}(1)));
+}
+static method localTest() → core::int {
+ function fnLocal(core::int a) → core::int {
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(fnLocal.call(a.{core::num::-}(1)));
+ }
+ return #C1;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 24);
+ exp::Expect::equals(self::localTest(), 24);
+}
+
+constants {
+ #C1 = 24
+}
diff --git a/pkg/front_end/testcases/general/issue45330.dart b/pkg/front_end/testcases/general/issue45330.dart
new file mode 100644
index 0000000..95ebfd0
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, 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.
+
+part 'issue45330_lib.dart';
+
+void genericMethod<T>() {}
+
+testInMain() {
+ genericMethod<void Function<T extends T>()>();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45330.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue45330.dart.textual_outline.expect
new file mode 100644
index 0000000..23719be
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+part 'issue45330_lib.dart';
+
+void genericMethod<T>() {}
+testInMain() {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45330.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue45330.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..eb6bf12
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+part 'issue45330_lib.dart';
+
+main() {}
+testInMain() {}
+void genericMethod<T>() {}
diff --git a/pkg/front_end/testcases/general/issue45330.dart.weak.expect b/pkg/front_end/testcases/general/issue45330.dart.weak.expect
new file mode 100644
index 0000000..c315320
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330.dart.weak.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/issue45330.dart:10:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/general/issue45330.dart:10:3: Error: A generic function type can't be used as a type argument.
+// Try using a non-generic function type.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/general/issue45330_lib.dart:35:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/general/issue45330_lib.dart:35:3: Error: A generic function type can't be used as a type argument.
+// Try using a non-generic function type.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void {}
+static method testInMain() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
diff --git a/pkg/front_end/testcases/general/issue45330.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue45330.dart.weak.outline.expect
new file mode 100644
index 0000000..fc3746d
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330.dart.weak.outline.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void
+ ;
+static method testInMain() → dynamic
+ ;
+static method main() → dynamic
+ ;
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/issue45330.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue45330.dart.weak.transformed.expect
new file mode 100644
index 0000000..c315320
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330.dart.weak.transformed.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/issue45330.dart:10:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/general/issue45330.dart:10:3: Error: A generic function type can't be used as a type argument.
+// Try using a non-generic function type.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/general/issue45330_lib.dart:35:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/general/issue45330_lib.dart:35:3: Error: A generic function type can't be used as a type argument.
+// Try using a non-generic function type.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void {}
+static method testInMain() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
diff --git a/pkg/front_end/testcases/general/issue45330_lib.dart b/pkg/front_end/testcases/general/issue45330_lib.dart
new file mode 100644
index 0000000..1772937
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45330_lib.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, 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.
+
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+
+part of 'issue45330.dart';
+
+testInPart() {
+ genericMethod<void Function<T extends T>()>();
+}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart b/pkg/front_end/testcases/generic_metadata/issue45330.dart
new file mode 100644
index 0000000..95ebfd0
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, 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.
+
+part 'issue45330_lib.dart';
+
+void genericMethod<T>() {}
+
+testInMain() {
+ genericMethod<void Function<T extends T>()>();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.strong.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.strong.expect
new file mode 100644
index 0000000..bf8ebf6
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.strong.expect
@@ -0,0 +1,26 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/generic_metadata/issue45330.dart:10:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/generic_metadata/issue45330_lib.dart:35:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void {}
+static method testInMain() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.strong.transformed.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.strong.transformed.expect
new file mode 100644
index 0000000..bf8ebf6
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.strong.transformed.expect
@@ -0,0 +1,26 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/generic_metadata/issue45330.dart:10:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/generic_metadata/issue45330_lib.dart:35:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void {}
+static method testInMain() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.textual_outline.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.textual_outline.expect
new file mode 100644
index 0000000..23719be
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+part 'issue45330_lib.dart';
+
+void genericMethod<T>() {}
+testInMain() {}
+main() {}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..eb6bf12
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+part 'issue45330_lib.dart';
+
+main() {}
+testInMain() {}
+void genericMethod<T>() {}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.expect
new file mode 100644
index 0000000..bf8ebf6
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.expect
@@ -0,0 +1,26 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/generic_metadata/issue45330.dart:10:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/generic_metadata/issue45330_lib.dart:35:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void {}
+static method testInMain() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.outline.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.outline.expect
new file mode 100644
index 0000000..fc3746d
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.outline.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void
+ ;
+static method testInMain() → dynamic
+ ;
+static method main() → dynamic
+ ;
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.transformed.expect b/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.transformed.expect
new file mode 100644
index 0000000..bf8ebf6
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330.dart.weak.transformed.expect
@@ -0,0 +1,26 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/generic_metadata/issue45330.dart:10:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+// pkg/front_end/testcases/generic_metadata/issue45330_lib.dart:35:31: Error: Type 'T' is a bound of itself via 'T'.
+// Try breaking the cycle by removing at least on of the 'extends' clauses in the cycle.
+// genericMethod<void Function<T extends T>()>();
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+part issue45330_lib.dart;
+static method genericMethod<T extends core::Object? = dynamic>() → void {}
+static method testInMain() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///issue45330_lib.dart */ testInPart() → dynamic {
+ self::genericMethod<<T extends invalid-type = invalid-type>() → void>();
+}
diff --git a/pkg/front_end/testcases/generic_metadata/issue45330_lib.dart b/pkg/front_end/testcases/generic_metadata/issue45330_lib.dart
new file mode 100644
index 0000000..1772937
--- /dev/null
+++ b/pkg/front_end/testcases/generic_metadata/issue45330_lib.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, 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.
+
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+// Long comment to increase error offset.
+
+part of 'issue45330.dart';
+
+testInPart() {
+ genericMethod<void Function<T extends T>()>();
+}
diff --git a/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart b/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
index d30310a..bd471ac 100644
--- a/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
+++ b/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
@@ -2,83 +2,9 @@
// 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.
// VMOptions=--verbose_debug --enable-isolate-groups --experimental-enable-isolate-groups-jit
-import 'dart:async';
-import 'dart:isolate' as dart_isolate;
-import 'package:observatory/service_io.dart';
-import 'package:test/test.dart';
-import 'service_test_common.dart';
-import 'test_helper.dart';
-import 'dart:developer';
-
-const int LINE_A = 18;
-const int LINE_B = 25;
-const int LINE_C = 29;
-
-foo(args) { // LINE_A
- final dart_isolate.SendPort sendPort = args[0] as dart_isolate.SendPort;
- sendPort.send('reply from foo');
-}
-
-testMain() async {
- final rpResponse = dart_isolate.ReceivePort();
- debugger(); // LINE_B
- await dart_isolate.Isolate.spawn(foo, [rpResponse.sendPort]);
- await rpResponse.first;
- rpResponse.close();
- debugger(); // LINE_C
-}
-
-final completerAtFoo = Completer();
-
-final tests = <IsolateTest>[
- hasPausedAtStart,
- resumeIsolate,
- hasStoppedAtBreakpoint,
- stoppedAtLine(LINE_B + 1),
- (Isolate isolate) async {
- // Set up a listener to wait for child isolate launch and breakpoint events.
- final stream = await isolate.vm.getEventStream(VM.kDebugStream);
- var childIsolate;
- var subscription;
- subscription = stream.listen((ServiceEvent event) async {
- switch (event.kind) {
- case ServiceEvent.kPauseStart:
- childIsolate = event.isolate!;
- await childIsolate.reload();
-
- Library rootLib = await childIsolate.rootLibrary.load() as Library;
- final foo = rootLib.functions.singleWhere((f) => f.name == 'foo');
- final bpt = await childIsolate.addBreakpointAtEntry(foo);
-
- expect(bpt is Breakpoint, isTrue);
- childIsolate.resume();
- break;
- case ServiceEvent.kPauseBreakpoint:
- if (childIsolate == event.isolate) {
- ServiceMap stack = await childIsolate.getStack();
- Frame top = stack['frames'][0];
- Script script = await top.location!.script.load() as Script;
- expect(script.tokenToLine(top.location!.tokenPos), equals(LINE_A));
-
- childIsolate.resume();
- subscription.cancel();
- completerAtFoo.complete();
- }
- break;
- }
- });
- },
- resumeIsolate,
- (Isolate isolate) async {
- await completerAtFoo.future;
- },
- hasStoppedAtBreakpoint,
- stoppedAtLine(LINE_C + 1),
- resumeIsolate,
-];
+import 'break_on_function_many_child_isolates_test.dart';
main(args) async {
- runIsolateTests(args, tests,
- testeeConcurrent: testMain, pause_on_start: true);
+ await runIsolateBreakpointPauseTest(args, /*nIsolates=*/ 1);
}
diff --git a/runtime/observatory/tests/service/break_on_function_many_child_isolates_test.dart b/runtime/observatory/tests/service/break_on_function_many_child_isolates_test.dart
new file mode 100644
index 0000000..916e6f9
--- /dev/null
+++ b/runtime/observatory/tests/service/break_on_function_many_child_isolates_test.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, 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.
+// VMOptions=--verbose_debug --enable-isolate-groups --experimental-enable-isolate-groups-jit
+//
+// Tests breakpoint pausing and resuming with many isolates running and pausing
+// simultaneously.
+
+import 'dart:async';
+import 'dart:developer';
+import 'dart:isolate' as dart_isolate;
+
+import 'package:observatory/service_io.dart';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 23;
+const int LINE_B = 35;
+const int LINE_C = 41;
+
+foo(args) { // LINE_A
+ print('${dart_isolate.Isolate.current.debugName}: $args');
+ final sendPort = args[0] as dart_isolate.SendPort;
+ final int i = args[1] as int;
+ sendPort.send('reply from foo: $i');
+}
+
+int nIsolates = -1;
+
+testMain() async {
+ final rps = List<dart_isolate.ReceivePort>.generate(
+ nIsolates, (i) => dart_isolate.ReceivePort());
+ debugger(); // LINE_B
+ for (int i = 0; i < nIsolates; i++) {
+ await dart_isolate.Isolate.spawn(foo, [rps[i].sendPort, i],
+ debugName: "foo$i");
+ }
+ print(await Future.wait(rps.map((rp) => rp.first)));
+ debugger(); // LINE_C
+}
+
+final completerAtFoo = List<Completer>.generate(nIsolates, (_) => Completer());
+int completerCount = 0;
+
+final tests = <IsolateTest>[
+ hasPausedAtStart,
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_B + 1),
+ (Isolate isolate) async {
+ // Set up a listener to wait for child isolate launch and breakpoint events.
+ final stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) async {
+ switch (event.kind) {
+ case ServiceEvent.kPauseStart:
+ final childIsolate = event.isolate!;
+ await childIsolate.reload();
+
+ for (Library lib in childIsolate.libraries) {
+ await lib.load();
+ if (lib.uri!
+ .endsWith('break_on_function_many_child_isolates_test.dart')) {
+ final foo = lib.functions.singleWhere((f) => f.name == 'foo');
+ final bpt = await childIsolate.addBreakpointAtEntry(foo);
+
+ expect(bpt is Breakpoint, isTrue);
+ break;
+ }
+ }
+ childIsolate.resume();
+ break;
+ case ServiceEvent.kPauseBreakpoint:
+ final name = event.isolate!.name!;
+ if (!name.startsWith('foo')) {
+ break;
+ }
+ final childIsolate = event.isolate;
+ final ndx = int.parse(name.substring('foo'.length));
+ final stack = await childIsolate!.getStack();
+ final top = stack['frames'][0];
+ final script = await top.location.script.load() as Script;
+ expect(script.tokenToLine(top.location.tokenPos), equals(LINE_A));
+
+ childIsolate.resume();
+ if ((++completerCount) == nIsolates) {
+ subscription.cancel();
+ }
+ completerAtFoo[ndx].complete();
+ break;
+ }
+ });
+ },
+ resumeIsolate,
+ (Isolate isolate) async {
+ await Future.wait(completerAtFoo.map((c) => c.future));
+ },
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_C + 1),
+ resumeIsolate,
+];
+
+Future runIsolateBreakpointPauseTest(args, nIsolates_) {
+ nIsolates = nIsolates_;
+ return runIsolateTests(args, tests,
+ testeeConcurrent: testMain, pause_on_start: true);
+}
+
+main(args) async {
+ await runIsolateBreakpointPauseTest(args, /*nIsolates=*/ 30);
+}
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 600d2df..1a1d71d 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -59,6 +59,7 @@
break_on_async_function_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_default_constructor_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_function_child_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
+break_on_function_many_child_isolates_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_function_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_async_break_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_in_package_parts_class_file_uri_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/observatory_2/tests/service_2/break_on_function_child_isolate_test.dart b/runtime/observatory_2/tests/service_2/break_on_function_child_isolate_test.dart
new file mode 100644
index 0000000..bd471ac
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/break_on_function_child_isolate_test.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, 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.
+// VMOptions=--verbose_debug --enable-isolate-groups --experimental-enable-isolate-groups-jit
+
+import 'break_on_function_many_child_isolates_test.dart';
+
+main(args) async {
+ await runIsolateBreakpointPauseTest(args, /*nIsolates=*/ 1);
+}
diff --git a/runtime/observatory_2/tests/service_2/break_on_function_many_child_isolates_test.dart b/runtime/observatory_2/tests/service_2/break_on_function_many_child_isolates_test.dart
new file mode 100644
index 0000000..b527cb54
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/break_on_function_many_child_isolates_test.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, 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.
+// VMOptions=--verbose_debug --enable-isolate-groups --experimental-enable-isolate-groups-jit
+//
+// Tests breakpoint pausing and resuming with many isolates running and pausing
+// simultaneously.
+
+import 'dart:async';
+import 'dart:developer';
+import 'dart:isolate' as dart_isolate;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 23;
+const int LINE_B = 35;
+const int LINE_C = 41;
+
+foo(args) { // LINE_A
+ print('${dart_isolate.Isolate.current.debugName}: $args');
+ final sendPort = args[0] as dart_isolate.SendPort;
+ final int i = args[1] as int;
+ sendPort.send('reply from foo: $i');
+}
+
+int nIsolates = -1;
+
+testMain() async {
+ final rps = List<dart_isolate.ReceivePort>.generate(
+ nIsolates, (i) => dart_isolate.ReceivePort());
+ debugger(); // LINE_B
+ for (int i = 0; i < nIsolates; i++) {
+ await dart_isolate.Isolate.spawn(foo, [rps[i].sendPort, i],
+ debugName: "foo$i");
+ }
+ print(await Future.wait(rps.map((rp) => rp.first)));
+ debugger(); // LINE_C
+}
+
+final completerAtFoo = List<Completer>.generate(nIsolates, (_) => Completer());
+int completerCount = 0;
+
+final tests = <IsolateTest>[
+ hasPausedAtStart,
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_B + 1),
+ (Isolate isolate) async {
+ // Set up a listener to wait for child isolate launch and breakpoint events.
+ final stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) async {
+ switch (event.kind) {
+ case ServiceEvent.kPauseStart:
+ final childIsolate = event.isolate;
+ await childIsolate.reload();
+
+ for (Library lib in childIsolate.libraries) {
+ await lib.load();
+ if (lib.uri
+ .endsWith('break_on_function_many_child_isolates_test.dart')) {
+ final foo = lib.functions.singleWhere((f) => f.name == 'foo');
+ final bpt = await childIsolate.addBreakpointAtEntry(foo);
+
+ expect(bpt is Breakpoint, isTrue);
+ break;
+ }
+ }
+ childIsolate.resume();
+ break;
+ case ServiceEvent.kPauseBreakpoint:
+ final name = event.isolate.name;
+ if (!name.startsWith('foo')) {
+ break;
+ }
+ final childIsolate = event.isolate;
+ final ndx = int.parse(name.substring('foo'.length));
+ final stack = await childIsolate.getStack();
+ final top = stack['frames'][0];
+ final script = await top.location.script.load() as Script;
+ expect(script.tokenToLine(top.location.tokenPos), equals(LINE_A));
+
+ childIsolate.resume();
+ if ((++completerCount) == nIsolates) {
+ subscription.cancel();
+ }
+ completerAtFoo[ndx].complete();
+ break;
+ }
+ });
+ },
+ resumeIsolate,
+ (Isolate isolate) async {
+ await Future.wait(completerAtFoo.map((c) => c.future));
+ },
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_C + 1),
+ resumeIsolate,
+];
+
+Future runIsolateBreakpointPauseTest(args, nIsolates_) {
+ nIsolates = nIsolates_;
+ return runIsolateTests(args, tests,
+ testeeConcurrent: testMain, pause_on_start: true);
+}
+
+main(args) async {
+ await runIsolateBreakpointPauseTest(args, /*nIsolates=*/ 30);
+}
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index a04fc9c..0c4696e 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -59,6 +59,7 @@
break_on_async_function_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_default_constructor_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_function_child_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
+break_on_function_many_child_isolates_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_function_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_async_break_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_in_package_parts_class_file_uri_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index 4ca1604..9b5c55e 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -603,6 +603,23 @@
auto install_code_fun = [&]() {
*result =
FinalizeCompilation(&assembler, &graph_compiler, flow_graph);
+#if !defined(PRODUCT)
+ // Isolate debuggers need to be notified of compiled function right
+ // away as code is installed because there might be latent breakpoints
+ // in compiled function, which have to be activated before functions
+ // code is executed. Otherwise concurrently running isolates might
+ // execute code before its patched and miss a need to pause at a
+ // breakpoint.
+ if (!result->IsNull()) {
+ if (!function.HasOptimizedCode()) {
+ thread()->isolate_group()->ForEachIsolate(
+ [&](Isolate* isolate) {
+ isolate->debugger()->NotifyCompilation(function);
+ },
+ /*at_safepoint=*/true);
+ }
+ }
+#endif
};
// Grab write program_lock outside of potential safepoint, that lock
@@ -632,15 +649,6 @@
// Must be called outside of safepoint.
Code::NotifyCodeObservers(function, *result, optimized());
-#if !defined(PRODUCT)
- if (!function.HasOptimizedCode()) {
- // TODO(dartbug.com/36097): We might need to adjust this once we start
- // adding debugging support to --enable-isolate-groups.
- thread()->isolate_group()->ForEachIsolate([&](Isolate* isolate) {
- isolate->debugger()->NotifyCompilation(function);
- });
- }
-#endif
if (FLAG_disassemble && FlowGraphPrinter::ShouldPrint(function)) {
Disassembler::DisassembleCode(function, *result, optimized());
} else if (FLAG_disassemble_optimized && optimized() &&
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 643e324..47dc4cd 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1455,7 +1455,7 @@
token_pos_(token_pos),
pc_(pc),
line_number_(-1),
- is_enabled_(false),
+ enabled_count_(0),
next_(NULL),
breakpoint_kind_(kind),
saved_value_(Code::null()) {
@@ -1501,17 +1501,17 @@
}
void CodeBreakpoint::Enable() {
- if (!is_enabled_) {
+ if (enabled_count_ == 0) {
PatchCode();
}
- ASSERT(is_enabled_);
+ ++enabled_count_;
}
void CodeBreakpoint::Disable() {
- if (is_enabled_) {
+ if (enabled_count_ == 1) {
RestoreCode();
}
- ASSERT(!is_enabled_);
+ --enabled_count_;
}
bool CodeBreakpoint::HasBreakpointLocation(
@@ -1599,13 +1599,13 @@
}
while (breakpoint_locations_ != nullptr) {
BreakpointLocation* loc = breakpoint_locations_;
- group_debugger()->DisableCodeBreakpointsFor(loc);
+ group_debugger()->UnlinkCodeBreakpoints(loc);
breakpoint_locations_ = breakpoint_locations_->next();
delete loc;
}
while (latent_locations_ != nullptr) {
BreakpointLocation* loc = latent_locations_;
- group_debugger()->DisableCodeBreakpointsFor(loc);
+ group_debugger()->UnlinkCodeBreakpoints(loc);
latent_locations_ = latent_locations_->next();
delete loc;
}
@@ -2510,29 +2510,26 @@
}
if (lowest_pc_offset != kUwordMax) {
uword lowest_pc = code.PayloadStart() + lowest_pc_offset;
+ SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
if (code_bpt == nullptr) {
- SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
- code_bpt = GetCodeBreakpoint(lowest_pc);
- if (code_bpt == nullptr) {
- // No code breakpoint for this code exists; create one.
- code_bpt =
- new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind);
- if (FLAG_verbose_debug) {
- OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px
- " offset %#" Px "\n",
- loc->token_pos_.ToCString(), lowest_pc,
- lowest_pc - code.PayloadStart());
- }
- RegisterCodeBreakpoint(code_bpt);
- } else {
- if (FLAG_verbose_debug) {
- OS::PrintErr(
- "Adding location to existing code breakpoint at pos %s pc %#" Px
- " offset %#" Px "\n",
- loc->token_pos_.ToCString(), lowest_pc,
- lowest_pc - code.PayloadStart());
- }
+ // No code breakpoint for this code exists; create one.
+ code_bpt =
+ new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind);
+ if (FLAG_verbose_debug) {
+ OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px
+ " offset %#" Px "\n",
+ loc->token_pos_.ToCString(), lowest_pc,
+ lowest_pc - code.PayloadStart());
+ }
+ RegisterCodeBreakpoint(code_bpt);
+ } else {
+ if (FLAG_verbose_debug) {
+ OS::PrintErr(
+ "Adding location to existing code breakpoint at pos %s pc %#" Px
+ " offset %#" Px "\n",
+ loc->token_pos_.ToCString(), lowest_pc,
+ lowest_pc - code.PayloadStart());
}
}
code_bpt->AddBreakpointLocation(loc);
@@ -2913,7 +2910,7 @@
void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
bool any_enabled = loc->AnyEnabled();
- SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
+ SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) {
if (cbpt->HasBreakpointLocation(loc)) {
@@ -2927,17 +2924,6 @@
}
}
-void GroupDebugger::DisableCodeBreakpointsFor(BreakpointLocation* location) {
- SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
- CodeBreakpoint* cbpt = code_breakpoints_;
- while (cbpt != nullptr) {
- if (cbpt->HasBreakpointLocation(location)) {
- cbpt->Disable();
- }
- cbpt = cbpt->next();
- }
-}
-
Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
bool single_shot) {
ASSERT(!target_function.IsNull());
@@ -4174,6 +4160,7 @@
// TODO(hausner): Could potentially make this faster by checking
// whether the call target at pc is a debugger stub.
bool GroupDebugger::HasActiveBreakpoint(uword pc) {
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
return (cbpt != nullptr) && (cbpt->IsEnabled());
}
@@ -4213,6 +4200,7 @@
}
CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
if (cbpt != NULL) {
return cbpt->OrigStubAddress();
@@ -4305,14 +4293,12 @@
// should be hit before it gets deleted.
void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
ASSERT(bpt_location != nullptr);
- SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
+ SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* curr_bpt = code_breakpoints_;
while (curr_bpt != nullptr) {
if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
- if (curr_bpt->HasNoBreakpointLocations()) {
- curr_bpt->Disable();
- needs_breakpoint_cleanup_ = true;
- }
+ curr_bpt->Disable();
+ needs_breakpoint_cleanup_ = true;
}
curr_bpt = curr_bpt->next();
}
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index c833e5d..ff044ed 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -203,6 +203,12 @@
// There may be more than one BreakpointLocation associated with CodeBreakpoint,
// one for for every isolate in a group that sets a breakpoint at particular
// code location represented by the CodeBreakpoint.
+// Each BreakpointLocation might be enabled/disabled based on whether it has
+// any actual breakpoints associated with it.
+// The CodeBreakpoint is enabled if it has any such BreakpointLocations
+// associated with it.
+// The class is not thread-safe - users of this class need to ensure the access
+// is synchronized, guarded by mutexes.
class CodeBreakpoint {
public:
CodeBreakpoint(const Code& code,
@@ -226,7 +232,7 @@
void Enable();
void Disable();
- bool IsEnabled() const { return is_enabled_; }
+ bool IsEnabled() const { return enabled_count_ > 0; }
CodePtr OrigStubAddress() const;
@@ -248,7 +254,7 @@
TokenPosition token_pos_;
uword pc_;
intptr_t line_number_;
- bool is_enabled_;
+ int enabled_count_; // incremented for every enabled breakpoint location
// Breakpoint locations from different debuggers/isolates that
// point to this code breakpoint.
@@ -521,7 +527,6 @@
bool HasBreakpointInCode(const Code& code);
void SyncBreakpointLocation(BreakpointLocation* loc);
- void DisableCodeBreakpointsFor(BreakpointLocation* loc);
void Pause();
diff --git a/runtime/vm/debugger_arm.cc b/runtime/vm/debugger_arm.cc
index 7fe5605..675c98e 100644
--- a/runtime/vm/debugger_arm.cc
+++ b/runtime/vm/debugger_arm.cc
@@ -20,7 +20,7 @@
}
void CodeBreakpoint::PatchCode() {
- ASSERT(!is_enabled_);
+ ASSERT(!IsEnabled());
Code& stub_target = Code::Handle();
switch (breakpoint_kind_) {
case UntaggedPcDescriptors::kIcCall:
@@ -38,11 +38,10 @@
const Code& code = Code::Handle(code_);
saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
CodePatcher::PatchStaticCallAt(pc_, code, stub_target);
- is_enabled_ = true;
}
void CodeBreakpoint::RestoreCode() {
- ASSERT(is_enabled_);
+ ASSERT(IsEnabled());
const Code& code = Code::Handle(code_);
switch (breakpoint_kind_) {
case UntaggedPcDescriptors::kIcCall:
@@ -54,7 +53,6 @@
default:
UNREACHABLE();
}
- is_enabled_ = false;
}
#endif // !PRODUCT
diff --git a/runtime/vm/debugger_arm64.cc b/runtime/vm/debugger_arm64.cc
index d3f251b..17cb472 100644
--- a/runtime/vm/debugger_arm64.cc
+++ b/runtime/vm/debugger_arm64.cc
@@ -20,7 +20,7 @@
}
void CodeBreakpoint::PatchCode() {
- ASSERT(!is_enabled_);
+ ASSERT(!IsEnabled());
const Code& code = Code::Handle(code_);
switch (breakpoint_kind_) {
case UntaggedPcDescriptors::kIcCall: {
@@ -45,11 +45,10 @@
default:
UNREACHABLE();
}
- is_enabled_ = true;
}
void CodeBreakpoint::RestoreCode() {
- ASSERT(is_enabled_);
+ ASSERT(IsEnabled());
const Code& code = Code::Handle(code_);
switch (breakpoint_kind_) {
case UntaggedPcDescriptors::kIcCall: {
@@ -68,7 +67,6 @@
default:
UNREACHABLE();
}
- is_enabled_ = false;
}
#endif // !PRODUCT
diff --git a/runtime/vm/debugger_ia32.cc b/runtime/vm/debugger_ia32.cc
index f9de84f..f244a34 100644
--- a/runtime/vm/debugger_ia32.cc
+++ b/runtime/vm/debugger_ia32.cc
@@ -24,7 +24,7 @@
}
void CodeBreakpoint::PatchCode() {
- ASSERT(!is_enabled_);
+ ASSERT(!IsEnabled());
auto thread = Thread::Current();
auto zone = thread->zone();
const Code& code = Code::Handle(zone, code_);
@@ -52,11 +52,10 @@
saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
CodePatcher::PatchStaticCallAt(pc_, code, stub_target);
});
- is_enabled_ = true;
}
void CodeBreakpoint::RestoreCode() {
- ASSERT(is_enabled_);
+ ASSERT(IsEnabled());
auto thread = Thread::Current();
auto zone = thread->zone();
const Code& code = Code::Handle(zone, code_);
@@ -74,7 +73,6 @@
UNREACHABLE();
}
});
- is_enabled_ = false;
}
#endif // !PRODUCT
diff --git a/runtime/vm/debugger_x64.cc b/runtime/vm/debugger_x64.cc
index 7a0e046..a6161a0 100644
--- a/runtime/vm/debugger_x64.cc
+++ b/runtime/vm/debugger_x64.cc
@@ -21,7 +21,7 @@
}
void CodeBreakpoint::PatchCode() {
- ASSERT(!is_enabled_);
+ ASSERT(!IsEnabled());
Code& stub_target = Code::Handle();
switch (breakpoint_kind_) {
case UntaggedPcDescriptors::kIcCall:
@@ -39,11 +39,10 @@
const Code& code = Code::Handle(code_);
saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
CodePatcher::PatchPoolPointerCallAt(pc_, code, stub_target);
- is_enabled_ = true;
}
void CodeBreakpoint::RestoreCode() {
- ASSERT(is_enabled_);
+ ASSERT(IsEnabled());
const Code& code = Code::Handle(code_);
switch (breakpoint_kind_) {
case UntaggedPcDescriptors::kIcCall:
@@ -56,7 +55,6 @@
default:
UNREACHABLE();
}
- is_enabled_ = false;
}
#endif // !PRODUCT
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 95d3875..d82c602 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -3011,10 +3011,7 @@
ASSERT(!unoptimized_code.IsNull());
// The switch to unoptimized code may have already occurred.
if (function.HasOptimizedCode()) {
- SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
- if (function.HasOptimizedCode()) {
- function.SwitchToUnoptimizedCode();
- }
+ function.SwitchToUnoptimizedCode();
}
if (frame->IsMarkedForLazyDeopt()) {
@@ -3048,23 +3045,29 @@
// Currently checks only that all optimized frames have kDeoptIndex
// and unoptimized code has the kDeoptAfter.
void DeoptimizeFunctionsOnStack() {
- auto isolate_group = IsolateGroup::Current();
+ auto thread = Thread::Current();
+ // Have to grab program_lock before stopping everybody else.
+ SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+
+ auto isolate_group = thread->isolate_group();
isolate_group->RunWithStoppedMutators([&]() {
Code& optimized_code = Code::Handle();
- isolate_group->ForEachIsolate([&](Isolate* isolate) {
- auto mutator_thread = isolate->mutator_thread();
- DartFrameIterator iterator(
- mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
- StackFrame* frame = iterator.NextFrame();
- while (frame != nullptr) {
- optimized_code = frame->LookupDartCode();
- if (optimized_code.is_optimized() &&
- !optimized_code.is_force_optimized()) {
- DeoptimizeAt(mutator_thread, optimized_code, frame);
- }
- frame = iterator.NextFrame();
- }
- });
+ isolate_group->ForEachIsolate(
+ [&](Isolate* isolate) {
+ auto mutator_thread = isolate->mutator_thread();
+ DartFrameIterator iterator(
+ mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
+ StackFrame* frame = iterator.NextFrame();
+ while (frame != nullptr) {
+ optimized_code = frame->LookupDartCode();
+ if (optimized_code.is_optimized() &&
+ !optimized_code.is_force_optimized()) {
+ DeoptimizeAt(mutator_thread, optimized_code, frame);
+ }
+ frame = iterator.NextFrame();
+ }
+ },
+ /*at_safepoint=*/true);
});
}
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index 609f66e..0a48da5 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -86,6 +86,8 @@
//
// Locking notes:
// The following locks are used by the timeline system:
+// - |Timeline::recorder_lock_| This lock is held whenever Timeline::recorder()
+// is accessed or modified.
// - |TimelineEventRecorder::lock_| This lock is held whenever a
// |TimelineEventBlock| is being requested or reclaimed.
// - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s
@@ -94,9 +96,10 @@
// |Thread|s.
//
// Locks must always be taken in the following order:
-// |Thread::thread_list_lock_|
-// |Thread::timeline_block_lock_|
-// |TimelineEventRecorder::lock_|
+// |Timeline::recorder_lock_|
+// |Thread::thread_list_lock_|
+// |Thread::timeline_block_lock_|
+// |TimelineEventRecorder::lock_|
//
static TimelineEventRecorder* CreateTimelineRecorder() {
@@ -205,6 +208,10 @@
}
void Timeline::Init() {
+ if (recorder_lock_ == nullptr) {
+ recorder_lock_ = new Mutex();
+ }
+ MutexLocker ml(recorder_lock_);
ASSERT(recorder_ == NULL);
recorder_ = CreateTimelineRecorder();
ASSERT(recorder_ != NULL);
@@ -217,6 +224,8 @@
}
void Timeline::Cleanup() {
+ ASSERT(recorder_lock_ != nullptr);
+ MutexLocker ml(recorder_lock_);
ASSERT(recorder_ != NULL);
#ifndef PRODUCT
@@ -243,6 +252,12 @@
}
void Timeline::ReclaimCachedBlocksFromThreads() {
+ ASSERT(recorder_lock_ != nullptr);
+ MutexLocker ml(recorder_lock_);
+ ReclaimCachedBlocksFromThreadsLocked();
+}
+
+void Timeline::ReclaimCachedBlocksFromThreadsLocked() {
TimelineEventRecorder* recorder = Timeline::recorder();
if (recorder == NULL) {
return;
@@ -274,6 +289,8 @@
}
void Timeline::PrintFlagsToJSON(JSONStream* js) {
+ ASSERT(recorder_lock_ != nullptr);
+ MutexLocker ml(recorder_lock_);
JSONObject obj(js);
obj.AddProperty("type", "TimelineFlags");
TimelineEventRecorder* recorder = Timeline::recorder();
@@ -301,11 +318,13 @@
#endif
void Timeline::Clear() {
+ ASSERT(recorder_lock_ != nullptr);
+ MutexLocker ml(recorder_lock_);
TimelineEventRecorder* recorder = Timeline::recorder();
if (recorder == NULL) {
return;
}
- ReclaimCachedBlocksFromThreads();
+ ReclaimCachedBlocksFromThreadsLocked();
recorder->Clear();
}
@@ -390,6 +409,7 @@
TimelineEventRecorder* Timeline::recorder_ = NULL;
MallocGrowableArray<char*>* Timeline::enabled_streams_ = NULL;
+Mutex* Timeline::recorder_lock_ = nullptr;
#define TIMELINE_STREAM_DEFINE(name, fuchsia_name) \
TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, false);
@@ -556,6 +576,8 @@
}
void TimelineEvent::Complete() {
+ ASSERT(Timeline::recorder_lock() != nullptr);
+ MutexLocker ml(Timeline::recorder_lock());
TimelineEventRecorder* recorder = Timeline::recorder();
if (recorder != NULL) {
recorder->CompleteEvent(this);
@@ -775,6 +797,10 @@
}
TimelineEvent* TimelineStream::StartEvent() {
+ if (Timeline::recorder_lock() == nullptr) {
+ return nullptr;
+ }
+ MutexLocker ml(Timeline::recorder_lock());
TimelineEventRecorder* recorder = Timeline::recorder();
if (!enabled() || (recorder == NULL)) {
return NULL;
@@ -1137,6 +1163,7 @@
}
TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() {
+ MutexLocker ml(&lock_);
// Delete all blocks.
for (intptr_t i = 0; i < num_blocks_; i++) {
blocks_[i].Reset();
@@ -1334,6 +1361,7 @@
: head_(nullptr), tail_(nullptr), block_index_(0) {}
TimelineEventEndlessRecorder::~TimelineEventEndlessRecorder() {
+ MutexLocker ml(&lock_);
TimelineEventBlock* current = head_;
head_ = tail_ = nullptr;
@@ -1424,6 +1452,9 @@
#endif
void TimelineEventEndlessRecorder::Clear() {
+ OSThread* thread = OSThread::Current();
+ MutexLocker ml(thread->timeline_block_lock());
+ MutexLocker ml2(&lock_);
TimelineEventBlock* current = head_;
while (current != NULL) {
TimelineEventBlock* next = current->next();
@@ -1431,8 +1462,8 @@
current = next;
}
head_ = NULL;
+ tail_ = NULL;
block_index_ = 0;
- OSThread* thread = OSThread::Current();
thread->set_timeline_block(NULL);
}
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 38e739a..a1eb5d6 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -23,7 +23,7 @@
#if defined(__MAC_10_14) || defined (__IPHONE_12_0)
#define HOST_OS_SUPPORTS_SIGNPOST 1
#endif
-//signpost.h exists in macOS 10.14, iOS 12 or above
+// signpost.h exists in macOS 10.14, iOS 12 or above
#if defined(HOST_OS_SUPPORTS_SIGNPOST)
#include <os/signpost.h>
#else
@@ -123,15 +123,18 @@
class Timeline : public AllStatic {
public:
- // Initialize timeline system. Not thread safe.
+ // Initialize timeline system.
static void Init();
- // Cleanup timeline system. Not thread safe.
+ // Cleanup timeline system.
static void Cleanup();
- // Access the global recorder. Not thread safe.
+ // Access the global recorder. recorder_lock() must be held when accessing
+ // the returned |TimelineEventRecorder|.
static TimelineEventRecorder* recorder();
+ static Mutex* recorder_lock() { return recorder_lock_; }
+
// Reclaim all |TimelineEventBlocks|s that are cached by threads.
static void ReclaimCachedBlocksFromThreads();
@@ -158,8 +161,13 @@
#undef TIMELINE_STREAM_FLAGS
private:
+ // Reclaims all |TimelineEventBlocks|s that are cached by threads, assuming
+ // that lock_ is held.
+ static void ReclaimCachedBlocksFromThreadsLocked();
+
static TimelineEventRecorder* recorder_;
static MallocGrowableArray<char*>* enabled_streams_;
+ static Mutex* recorder_lock_;
#define TIMELINE_STREAM_DECLARE(name, fuchsia_name) \
static TimelineStream stream_##name##_;
diff --git a/tests/language/const_functions/const_functions_recursion_error_test.dart b/tests/language/const_functions/const_functions_recursion_error_test.dart
new file mode 100644
index 0000000..f4332a9
--- /dev/null
+++ b/tests/language/const_functions/const_functions_recursion_error_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, 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 recursive function calls for const functions which have a cycle in the
+// dependencies.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const dependsOnB = b;
+// ^
+// [cfe] Constant evaluation error:
+const b = fn(4);
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn(int a) {
+ if (a == 1) return dependsOnB;
+ return dependsOnB * fn(a - 1);
+}
diff --git a/tests/language/const_functions/const_functions_recursion_test.dart b/tests/language/const_functions/const_functions_recursion_test.dart
new file mode 100644
index 0000000..c1343b1
--- /dev/null
+++ b/tests/language/const_functions/const_functions_recursion_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2021, 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 recursive function calls for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const b = fn(4);
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn(int a) {
+ if (a == 1) return 1;
+ return a * fn(a - 1);
+}
+
+int localTest() {
+ int fnLocal(int a) {
+ if (a == 1) return 1;
+ return a * fnLocal(a - 1);
+ }
+
+ const c = fnLocal(4);
+ // ^^^^^^^^^^
+ // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+ return c;
+}
+
+void main() {
+ Expect.equals(b, 24);
+ Expect.equals(localTest(), 24);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 78dca80..5ed7e50 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 153
+PRERELEASE 154
PRERELEASE_PATCH 0
\ No newline at end of file