Version 2.13.0-195.0.dev
Merge commit 'a8590a287ec2e76b8c4155149d9a681df892767b' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4028c90..51cf065 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,48 @@
### Language
+* **Type aliases** [Non-function type aliases][]: Type aliases (names for
+ types introduced via the `typedef` keyword) were previously restricted
+ to only introduce names for function types. In this release, we
+ remove this restriction and allow type aliases to name any kind of type.
+
+ ```dart
+ import 'dart:convert';
+
+ typedef JsonMap = Map<String, dynamic>;
+
+ JsonMap parseJsonMap(String input) => json.decode(input) as JsonMap;
+ ```
+
+ In addition to being usable as type annotations, type aliases that name
+ class types can now also be used anywhere that the underlying class could be
+ used, allowing type aliases to be used to safely rename existing classes.
+
+ ```dart
+ class NewClassName<T> {
+ NewClassName.create(T x);
+ static NewClassName<T> mkOne<T>(T x) => NewClassName<T>.create(x);
+ }
+ @Deprecated("Use NewClassName instead")
+ typedef OldClassName<T> = NewClassName<T>;
+
+ class LegacyClass extends OldClassName<int> {
+ LegacyClass() : super.create(3);
+ }
+ OldClassName<int> legacyCode() {
+ var one = OldClassName.create(1);
+ var two = OldClassName.mkOne(2);
+ return LegacyClass();
+ }
+ ```
+
+ The new type alias feature is only available as part of the 2.13 [language
+ version](https://dart.dev/guides/language/evolution). To use this feature,
+ you must set the lower bound on the sdk constraint for your package to 2.13
+ or greater.
+
+ [Non-function type aliases]: https://github.com/dart-lang/language/blob/master/accepted/2.13/nonfunction-type-aliases/feature-specification.md
+
### Core libraries
#### `dart:collection`
diff --git a/pkg/analysis_server/test/analysis/get_hover_test.dart b/pkg/analysis_server/test/analysis/get_hover_test.dart
index fb78fb5..47d5d33 100644
--- a/pkg/analysis_server/test/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/analysis/get_hover_test.dart
@@ -19,8 +19,7 @@
}
@reflectiveTest
-class AnalysisHoverTest extends AbstractAnalysisTest
- with WithNonFunctionTypeAliasesMixin {
+class AnalysisHoverTest extends AbstractAnalysisTest {
Future<HoverInformation> prepareHover(String search) {
var offset = findOffset(search);
return prepareHoverAt(offset);
diff --git a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
index 8650731..7bab497 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
@@ -23,8 +23,7 @@
}
@reflectiveTest
-class AnalysisNotificationHighlightsTest extends HighlightsTestSupport
- with WithNonFunctionTypeAliasesMixin {
+class AnalysisNotificationHighlightsTest extends HighlightsTestSupport {
Future<void> test_ANNOTATION_hasArguments() async {
addTestFile('''
class AAA {
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index 8e52073..c99d007 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -21,8 +21,7 @@
});
}
-class AbstractNavigationTest extends AbstractAnalysisTest
- with WithNonFunctionTypeAliasesMixin {
+class AbstractNavigationTest extends AbstractAnalysisTest {
List<NavigationRegion> regions;
List<NavigationTarget> targets;
List<String> targetFiles;
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index f2f5cf8..ab75d6b 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -238,15 +238,3 @@
return serverChannel.sendRequest(request, throwOnError: throwOnError);
}
}
-
-mixin WithNonFunctionTypeAliasesMixin on AbstractAnalysisTest {
- @override
- void createProject({Map<String, String> packageRoots}) {
- addAnalysisOptionsFile('''
-analyzer:
- enable-experiment:
- - nonfunction-type-aliases
-''');
- super.createProject(packageRoots: packageRoots);
- }
-}
diff --git a/pkg/analysis_server/test/search/element_references_test.dart b/pkg/analysis_server/test/search/element_references_test.dart
index fba332c..1049186 100644
--- a/pkg/analysis_server/test/search/element_references_test.dart
+++ b/pkg/analysis_server/test/search/element_references_test.dart
@@ -9,13 +9,11 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../analysis_abstract.dart';
import 'abstract_search_domain.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ElementReferencesTest);
- defineReflectiveTests(ElementReferencesWithNonFunctionTypeAliasesTest);
});
}
@@ -23,8 +21,6 @@
class ElementReferencesTest extends AbstractSearchDomainTest {
Element searchElement;
- bool get hasNonFunctionTypeAliases => false;
-
void assertHasRef(SearchResultKind kind, String search, bool isPotential) {
assertHasResult(kind, search);
expect(result.isPotential, isPotential);
@@ -876,54 +872,11 @@
}
''');
await findElementReferences('F =', false);
- expect(
- searchElement.kind,
- hasNonFunctionTypeAliases
- ? ElementKind.TYPE_ALIAS
- : ElementKind.FUNCTION_TYPE_ALIAS,
- );
+ expect(searchElement.kind, ElementKind.TYPE_ALIAS);
expect(results, hasLength(1));
assertHasResult(SearchResultKind.REFERENCE, 'F f');
}
- Future<void> test_typeReference_typeAlias_legacy() async {
- addTestFile('''
-typedef F();
-main(F f) {
-}
-''');
- await findElementReferences('F()', false);
- expect(
- searchElement.kind,
- hasNonFunctionTypeAliases
- ? ElementKind.TYPE_ALIAS
- : ElementKind.FUNCTION_TYPE_ALIAS,
- );
- expect(results, hasLength(1));
- assertHasResult(SearchResultKind.REFERENCE, 'F f');
- }
-
- Future<void> test_typeReference_typeVariable() async {
- addTestFile('''
-class A<T> {
- T f;
- T m() => null;
-}
-''');
- await findElementReferences('T> {', false);
- expect(searchElement.kind, ElementKind.TYPE_PARAMETER);
- expect(results, hasLength(2));
- assertHasResult(SearchResultKind.REFERENCE, 'T f;');
- assertHasResult(SearchResultKind.REFERENCE, 'T m()');
- }
-}
-
-@reflectiveTest
-class ElementReferencesWithNonFunctionTypeAliasesTest
- extends ElementReferencesTest with WithNonFunctionTypeAliasesMixin {
- @override
- bool get hasNonFunctionTypeAliases => true;
-
Future<void> test_typeReference_typeAlias_interfaceType() async {
addTestFile('''
typedef A<T> = Map<int, T>;
@@ -941,4 +894,30 @@
expect(searchElement.kind, ElementKind.CLASS);
assertHasResult(SearchResultKind.REFERENCE, 'int,');
}
+
+ Future<void> test_typeReference_typeAlias_legacy() async {
+ addTestFile('''
+typedef F();
+main(F f) {
+}
+''');
+ await findElementReferences('F()', false);
+ expect(searchElement.kind, ElementKind.TYPE_ALIAS);
+ expect(results, hasLength(1));
+ assertHasResult(SearchResultKind.REFERENCE, 'F f');
+ }
+
+ Future<void> test_typeReference_typeVariable() async {
+ addTestFile('''
+class A<T> {
+ T f;
+ T m() => null;
+}
+''');
+ await findElementReferences('T> {', false);
+ expect(searchElement.kind, ElementKind.TYPE_PARAMETER);
+ expect(results, hasLength(2));
+ assertHasResult(SearchResultKind.REFERENCE, 'T f;');
+ assertHasResult(SearchResultKind.REFERENCE, 'T m()');
+ }
}
diff --git a/pkg/analysis_server/test/search/top_level_declarations_test.dart b/pkg/analysis_server/test/search/top_level_declarations_test.dart
index 7cda6cf..37a7cf8 100644
--- a/pkg/analysis_server/test/search/top_level_declarations_test.dart
+++ b/pkg/analysis_server/test/search/top_level_declarations_test.dart
@@ -80,8 +80,8 @@
await findTopLevelDeclarations('^[A-F]\$');
assertHasDeclaration(ElementKind.CLASS, 'A');
assertHasDeclaration(ElementKind.CLASS, 'B');
- assertHasDeclaration(ElementKind.FUNCTION_TYPE_ALIAS, 'C');
- assertHasDeclaration(ElementKind.FUNCTION_TYPE_ALIAS, 'D');
+ assertHasDeclaration(ElementKind.TYPE_ALIAS, 'C');
+ assertHasDeclaration(ElementKind.TYPE_ALIAS, 'D');
assertHasDeclaration(ElementKind.FUNCTION, 'E');
assertHasDeclaration(ElementKind.TOP_LEVEL_VARIABLE, 'F');
assertNoDeclaration(ElementKind.CLASS, 'ABC');
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index 5d7c6ec..74cac9d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -152,7 +152,7 @@
isExpired: IsExpired.nonfunction_type_aliases,
documentation: 'Type aliases define a <type>, not just a <functionType>',
experimentalReleaseVersion: null,
- releaseVersion: null,
+ releaseVersion: Version.parse('2.13.0'),
);
static final set_literals = ExperimentalFeature(
@@ -231,7 +231,7 @@
static const bool non_nullable = true;
/// Default state of the experiment "nonfunction-type-aliases"
- static const bool nonfunction_type_aliases = false;
+ static const bool nonfunction_type_aliases = true;
/// Default state of the experiment "set-literals"
static const bool set_literals = true;
diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
index e7d11dc..f774c07 100644
--- a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
+++ b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
@@ -87,7 +87,7 @@
ExperimentalFlag.extensionTypes: false,
ExperimentalFlag.genericMetadata: false,
ExperimentalFlag.nonNullable: true,
- ExperimentalFlag.nonfunctionTypeAliases: false,
+ ExperimentalFlag.nonfunctionTypeAliases: true,
ExperimentalFlag.setLiterals: true,
ExperimentalFlag.spreadCollections: true,
ExperimentalFlag.tripleShift: false,
diff --git a/runtime/vm/closure_functions_cache.cc b/runtime/vm/closure_functions_cache.cc
index e70d6ce..ff62680 100644
--- a/runtime/vm/closure_functions_cache.cc
+++ b/runtime/vm/closure_functions_cache.cc
@@ -172,7 +172,7 @@
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
- {
+ if (!thread->IsInStoppedMutatorsScope()) {
// The empty read locker scope will implicitly issue an acquire memory
// fence, which means any closure functions added so far will be visible and
// iterated further down.
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 1a9d77d..206aad7 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6965,12 +6965,14 @@
bool non_main_isolates_alive = false;
{
SafepointOperationScope safepoint(thread);
- group->ForEachIsolate([&](Isolate* isolate) {
- if (isolate != main_isolate) {
- Isolate::KillIfExists(isolate, Isolate::kKillMsg);
- non_main_isolates_alive = true;
- }
- });
+ group->ForEachIsolate(
+ [&](Isolate* isolate) {
+ if (isolate != main_isolate) {
+ Isolate::KillIfExists(isolate, Isolate::kKillMsg);
+ non_main_isolates_alive = true;
+ }
+ },
+ /*at_safepoint=*/true);
if (!non_main_isolates_alive) {
break;
}
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index d057516..592a7e5 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -460,27 +460,31 @@
}
bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
- SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
- CodeBreakpoint* cbpt = code_breakpoints_;
- while (cbpt != NULL) {
- if (func.ptr() == cbpt->function()) {
- return true;
+ auto thread = Thread::Current();
+ return RunUnderReadLockIfNeeded(thread, code_breakpoints_lock(), [&]() {
+ CodeBreakpoint* cbpt = code_breakpoints_;
+ while (cbpt != NULL) {
+ if (func.ptr() == cbpt->function()) {
+ return true;
+ }
+ cbpt = cbpt->next_;
}
- cbpt = cbpt->next_;
- }
- return false;
+ return false;
+ });
}
bool GroupDebugger::HasBreakpointInCode(const Code& code) {
- SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
- CodeBreakpoint* cbpt = code_breakpoints_;
- while (cbpt != NULL) {
- if (code.ptr() == cbpt->code_) {
- return true;
+ auto thread = Thread::Current();
+ return RunUnderReadLockIfNeeded(thread, code_breakpoints_lock(), [&]() {
+ CodeBreakpoint* cbpt = code_breakpoints_;
+ while (cbpt != NULL) {
+ if (code.ptr() == cbpt->code_) {
+ return true;
+ }
+ cbpt = cbpt->next_;
}
- cbpt = cbpt->next_;
- }
- return false;
+ return false;
+ });
}
void Debugger::PrintBreakpointsToJSONArray(JSONArray* jsarr) const {
@@ -3211,7 +3215,12 @@
// Going through BreakpointLocations of all isolates and debuggers looking
// for those that can be resolved and added code breakpoints at now.
- SafepointReadRwLocker sl(thread, breakpoint_locations_lock());
+ //
+ // The check below is used instead of breakpoint_locations_lock acquisition.
+ // We don't need to acquire the lock if always run with stopped mutators.
+ // We can't acquire the lock if we run with stopped mutators as that could
+ // result in deadlock.
+ RELEASE_ASSERT(thread->IsInStoppedMutatorsScope());
for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
BreakpointLocation* location = breakpoint_locations_.At(i);
if (EnsureLocationIsInFunction(zone, function, location)) {
@@ -3700,20 +3709,34 @@
single_stepping_set_.Remove(debugger);
}
-bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
- {
- // Check if function has any breakpoints.
- SafepointReadRwLocker sl(thread, breakpoint_locations_lock());
- Script& script = Script::Handle(thread->zone());
- for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
- BreakpointLocation* location = breakpoint_locations_.At(i);
- script = location->script();
- if (FunctionOverlaps(function, script, location->token_pos(),
- location->end_token_pos())) {
- return true;
- }
- }
+bool GroupDebugger::RunUnderReadLockIfNeededCallable(Thread* thread,
+ SafepointRwLock* rw_lock,
+ BoolCallable* callable) {
+ if (thread->IsInStoppedMutatorsScope()) {
+ return callable->Call();
}
+
+ SafepointReadRwLocker sl(thread, rw_lock);
+ return callable->Call();
+}
+
+bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
+ if (RunUnderReadLockIfNeeded(thread, breakpoint_locations_lock(), [&]() {
+ // Check if function has any breakpoints.
+ Script& script = Script::Handle(thread->zone());
+ for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+ BreakpointLocation* location = breakpoint_locations_.At(i);
+ script = location->script();
+ if (FunctionOverlaps(function, script, location->token_pos(),
+ location->end_token_pos())) {
+ return true;
+ }
+ }
+ return false;
+ })) {
+ return true;
+ }
+
// TODO(aam): do we have to iterate over both code breakpoints and
// breakpoint locations? Wouldn't be sufficient to iterate over only
// one list? Could you have a CodeBreakpoint without corresponding
@@ -3726,11 +3749,10 @@
}
bool GroupDebugger::IsDebugging(Thread* thread, const Function& function) {
- {
- SafepointReadRwLocker sl(thread, single_stepping_set_lock());
- if (!single_stepping_set_.IsEmpty()) {
- return true;
- }
+ if (!RunUnderReadLockIfNeeded(thread, single_stepping_set_lock(), [&]() {
+ return single_stepping_set_.IsEmpty();
+ })) {
+ return true;
}
return HasBreakpoint(thread, function);
}
@@ -3983,6 +4005,9 @@
}
if (FLAG_verbose_debug) {
+ // Lock is needed for CodeBreakpoint::ToCString().
+ SafepointReadRwLocker sl(Thread::Current(),
+ group_debugger()->code_breakpoints_lock());
OS::PrintErr(">>> hit %" Pd
" %s"
" (func %s token %s address %#" Px " offset %#" Px ")\n",
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index bad3a2b..bd2d0de 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -557,6 +557,28 @@
}
};
+class BoolCallable : public ValueObject {
+ public:
+ BoolCallable() {}
+ virtual ~BoolCallable() {}
+
+ virtual bool Call() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BoolCallable);
+};
+
+template <typename T>
+class LambdaBoolCallable : public BoolCallable {
+ public:
+ explicit LambdaBoolCallable(T& lambda) : lambda_(lambda) {}
+ bool Call() { return lambda_(); }
+
+ private:
+ T& lambda_;
+ DISALLOW_COPY_AND_ASSIGN(LambdaBoolCallable);
+};
+
class GroupDebugger {
public:
explicit GroupDebugger(IsolateGroup* isolate_group);
@@ -597,6 +619,10 @@
void VisitObjectPointers(ObjectPointerVisitor* visitor);
+ SafepointRwLock* code_breakpoints_lock() {
+ return code_breakpoints_lock_.get();
+ }
+
SafepointRwLock* breakpoint_locations_lock() {
return breakpoint_locations_lock_.get();
}
@@ -608,6 +634,18 @@
void UnregisterSingleSteppingDebugger(Thread* thread,
const Debugger* debugger);
+ bool RunUnderReadLockIfNeededCallable(Thread* thread,
+ SafepointRwLock* rw_lock,
+ BoolCallable* callable);
+
+ template <typename T>
+ bool RunUnderReadLockIfNeeded(Thread* thread,
+ SafepointRwLock* rw_lock,
+ T function) {
+ LambdaBoolCallable<T> callable(function);
+ return RunUnderReadLockIfNeededCallable(thread, rw_lock, &callable);
+ }
+
// Returns [true] if there is at least one breakpoint set in function or code.
// Checks for both user-defined and internal temporary breakpoints.
bool HasBreakpoint(Thread* thread, const Function& function);
@@ -628,9 +666,6 @@
std::unique_ptr<SafepointRwLock> single_stepping_set_lock_;
DebuggerSet single_stepping_set_;
- SafepointRwLock* code_breakpoints_lock() {
- return code_breakpoints_lock_.get();
- }
void RemoveUnlinkedCodeBreakpoints();
void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
diff --git a/runtime/vm/experimental_features.cc b/runtime/vm/experimental_features.cc
index e341195..57eb309 100644
--- a/runtime/vm/experimental_features.cc
+++ b/runtime/vm/experimental_features.cc
@@ -18,7 +18,7 @@
bool GetExperimentalFeatureDefault(ExperimentalFeature feature) {
constexpr bool kFeatureValues[] = {
- true, true, true, true, true, true,
+ true, true, true, true, true, true, true,
};
ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureValues));
return kFeatureValues[static_cast<int>(feature)];
@@ -26,9 +26,10 @@
const char* GetExperimentalFeatureName(ExperimentalFeature feature) {
constexpr const char* kFeatureNames[] = {
- "non-nullable", "extension-methods",
- "constant-update-2018", "control-flow-collections",
- "set-literals", "spread-collections",
+ "nonfunction-type-aliases", "non-nullable",
+ "extension-methods", "constant-update-2018",
+ "control-flow-collections", "set-literals",
+ "spread-collections",
};
ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureNames));
return kFeatureNames[static_cast<int>(feature)];
diff --git a/runtime/vm/experimental_features.h b/runtime/vm/experimental_features.h
index 8efd29e..d6816b3 100644
--- a/runtime/vm/experimental_features.h
+++ b/runtime/vm/experimental_features.h
@@ -14,6 +14,7 @@
namespace dart {
enum class ExperimentalFeature {
+ nonfunction_type_aliases,
non_nullable,
extension_methods,
constant_update_2018,
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index f730542..52d1f65 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -990,64 +990,70 @@
#ifndef PRODUCT
// For now we'll emit the same GC events on all isolates.
if (Service::gc_stream.enabled()) {
- isolate_group_->ForEachIsolate([&](Isolate* isolate) {
- if (!Isolate::IsSystemIsolate(isolate)) {
- ServiceEvent event(isolate, ServiceEvent::kGC);
- event.set_gc_stats(&stats_);
- Service::HandleEvent(&event);
- }
- });
+ isolate_group_->ForEachIsolate(
+ [&](Isolate* isolate) {
+ if (!Isolate::IsSystemIsolate(isolate)) {
+ ServiceEvent event(isolate, ServiceEvent::kGC);
+ event.set_gc_stats(&stats_);
+ Service::HandleEvent(&event);
+ }
+ },
+ /*at_safepoint=*/true);
}
#endif // !PRODUCT
if (Dart::gc_event_callback() != nullptr) {
- isolate_group_->ForEachIsolate([&](Isolate* isolate) {
- if (!Isolate::IsSystemIsolate(isolate)) {
- Dart_GCEvent event;
- auto isolate_id = Utils::CStringUniquePtr(
- OS::SCreate(nullptr, ISOLATE_SERVICE_ID_FORMAT_STRING,
- isolate->main_port()),
- std::free);
- int64_t isolate_uptime_micros = isolate->UptimeMicros();
+ isolate_group_->ForEachIsolate(
+ [&](Isolate* isolate) {
+ if (!Isolate::IsSystemIsolate(isolate)) {
+ Dart_GCEvent event;
+ auto isolate_id = Utils::CStringUniquePtr(
+ OS::SCreate(nullptr, ISOLATE_SERVICE_ID_FORMAT_STRING,
+ isolate->main_port()),
+ std::free);
+ int64_t isolate_uptime_micros = isolate->UptimeMicros();
- event.isolate_id = isolate_id.get();
- event.type = GCTypeToString(stats_.type_);
- event.reason = GCReasonToString(stats_.reason_);
+ event.isolate_id = isolate_id.get();
+ event.type = GCTypeToString(stats_.type_);
+ event.reason = GCReasonToString(stats_.reason_);
- // New space - Scavenger.
- {
- intptr_t new_space_collections = new_space_.collections();
+ // New space - Scavenger.
+ {
+ intptr_t new_space_collections = new_space_.collections();
- event.new_space.collections = new_space_collections;
- event.new_space.used = stats_.after_.new_.used_in_words * kWordSize;
- event.new_space.capacity =
- stats_.after_.new_.capacity_in_words * kWordSize;
- event.new_space.external =
- stats_.after_.new_.external_in_words * kWordSize;
- event.new_space.time =
- MicrosecondsToSeconds(new_space_.gc_time_micros());
- event.new_space.avg_collection_period =
- AvgCollectionPeriod(isolate_uptime_micros, new_space_collections);
- }
+ event.new_space.collections = new_space_collections;
+ event.new_space.used =
+ stats_.after_.new_.used_in_words * kWordSize;
+ event.new_space.capacity =
+ stats_.after_.new_.capacity_in_words * kWordSize;
+ event.new_space.external =
+ stats_.after_.new_.external_in_words * kWordSize;
+ event.new_space.time =
+ MicrosecondsToSeconds(new_space_.gc_time_micros());
+ event.new_space.avg_collection_period = AvgCollectionPeriod(
+ isolate_uptime_micros, new_space_collections);
+ }
- // Old space - Page.
- {
- intptr_t old_space_collections = old_space_.collections();
+ // Old space - Page.
+ {
+ intptr_t old_space_collections = old_space_.collections();
- event.old_space.collections = old_space_collections;
- event.old_space.used = stats_.after_.old_.used_in_words * kWordSize;
- event.old_space.capacity =
- stats_.after_.old_.capacity_in_words * kWordSize;
- event.old_space.external =
- stats_.after_.old_.external_in_words * kWordSize;
- event.old_space.time =
- MicrosecondsToSeconds(old_space_.gc_time_micros());
- event.old_space.avg_collection_period =
- AvgCollectionPeriod(isolate_uptime_micros, old_space_collections);
- }
+ event.old_space.collections = old_space_collections;
+ event.old_space.used =
+ stats_.after_.old_.used_in_words * kWordSize;
+ event.old_space.capacity =
+ stats_.after_.old_.capacity_in_words * kWordSize;
+ event.old_space.external =
+ stats_.after_.old_.external_in_words * kWordSize;
+ event.old_space.time =
+ MicrosecondsToSeconds(old_space_.gc_time_micros());
+ event.old_space.avg_collection_period = AvgCollectionPeriod(
+ isolate_uptime_micros, old_space_collections);
+ }
- (*Dart::gc_event_callback())(&event);
- }
- });
+ (*Dart::gc_event_callback())(&event);
+ }
+ },
+ /*at_safepoint=*/true);
}
}
diff --git a/runtime/vm/heap/weak_code.cc b/runtime/vm/heap/weak_code.cc
index c482fe7..44730a3 100644
--- a/runtime/vm/heap/weak_code.cc
+++ b/runtime/vm/heap/weak_code.cc
@@ -66,7 +66,8 @@
return;
#else
// Ensure mutators see empty code_objects only after code was deoptimized.
- SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+ DEBUG_ASSERT(
+ IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
if (code_objects.IsNull()) {
return;
@@ -76,20 +77,22 @@
// Deoptimize stacks and disable code with mutators stopped.
isolate_group->RunWithStoppedMutators([&]() {
Code& 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) {
- code = frame->LookupDartCode();
- if (IsOptimizedCode(code_objects, code)) {
- ReportDeoptimization(code);
- DeoptimizeAt(mutator_thread, 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) {
+ code = frame->LookupDartCode();
+ if (IsOptimizedCode(code_objects, code)) {
+ ReportDeoptimization(code);
+ DeoptimizeAt(mutator_thread, code, frame);
+ }
+ frame = iterator.NextFrame();
+ }
+ },
+ /*at_safepoint=*/true);
// Switch functions that use dependent code to unoptimized code.
WeakProperty& weak_property = WeakProperty::Handle();
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 495d026..69ae961 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1738,6 +1738,7 @@
#undef ISOLATE_METRIC_CONSTRUCTORS
#endif // !defined(PRODUCT)
start_time_micros_(OS::GetCurrentMonotonicMicros()),
+ message_notify_callback_(nullptr),
on_shutdown_callback_(Isolate::ShutdownCallback()),
on_cleanup_callback_(Isolate::CleanupCallback()),
random_(),
@@ -2731,20 +2732,27 @@
void IsolateGroup::ForEachIsolate(
std::function<void(Isolate* isolate)> function,
bool at_safepoint) {
+ auto thread = Thread::Current();
if (at_safepoint) {
- ASSERT(Thread::Current()->IsAtSafepoint() ||
- (Thread::Current()->task_kind() == Thread::kMutatorTask) ||
- (Thread::Current()->task_kind() == Thread::kMarkerTask) ||
- (Thread::Current()->task_kind() == Thread::kCompactorTask) ||
- (Thread::Current()->task_kind() == Thread::kScavengerTask));
+ ASSERT(thread->IsAtSafepoint() ||
+ (thread->task_kind() == Thread::kMutatorTask) ||
+ (thread->task_kind() == Thread::kMarkerTask) ||
+ (thread->task_kind() == Thread::kCompactorTask) ||
+ (thread->task_kind() == Thread::kScavengerTask));
for (Isolate* isolate : isolates_) {
function(isolate);
}
- } else {
- SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get());
+ return;
+ }
+ if (thread != nullptr && thread->IsAtSafepoint()) {
for (Isolate* isolate : isolates_) {
function(isolate);
}
+ return;
+ }
+ SafepointReadRwLocker ml(thread, isolates_lock_.get());
+ for (Isolate* isolate : isolates_) {
+ function(isolate);
}
}
@@ -2762,6 +2770,7 @@
Callable* otherwise,
bool use_force_growth_in_otherwise) {
auto thread = Thread::Current();
+ StoppedMutatorsScope stopped_mutators_scope(thread);
if (thread->IsMutatorThread() && !IsolateGroup::AreIsolateGroupsEnabled()) {
single_current_mutator->Call();
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 90f557a..fd19306 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -1024,11 +1024,11 @@
}
Dart_MessageNotifyCallback message_notify_callback() const {
- return message_notify_callback_;
+ return message_notify_callback_.load(std::memory_order_relaxed);
}
void set_message_notify_callback(Dart_MessageNotifyCallback value) {
- message_notify_callback_ = value;
+ message_notify_callback_.store(value, std::memory_order_release);
}
void set_on_shutdown_callback(Dart_IsolateShutdownCallback value) {
@@ -1594,7 +1594,7 @@
// All other fields go here.
int64_t start_time_micros_;
- Dart_MessageNotifyCallback message_notify_callback_ = nullptr;
+ std::atomic<Dart_MessageNotifyCallback> message_notify_callback_;
Dart_IsolateShutdownCallback on_shutdown_callback_ = nullptr;
Dart_IsolateCleanupCallback on_cleanup_callback_ = nullptr;
char* name_ = nullptr;
diff --git a/runtime/vm/lockers.cc b/runtime/vm/lockers.cc
index a39f0c6..c3b35f3 100644
--- a/runtime/vm/lockers.cc
+++ b/runtime/vm/lockers.cc
@@ -108,6 +108,12 @@
bool SafepointRwLock::EnterRead() {
// No need to safepoint if the current thread is not attached.
auto thread = Thread::Current();
+ // Attempt to acquire a lock while owning a safepoint could lead to a deadlock
+ // (some other thread might be forced to a safepoint while holding this lock).
+ ASSERT(thread == nullptr ||
+ !thread->isolate_group()->safepoint_handler()->IsOwnedByTheThread(
+ thread));
+
const bool can_block_without_safepoint = thread == nullptr;
bool acquired_read_lock = false;
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index c76464f..87b76c0 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -570,6 +570,10 @@
bool IsInNoReloadScope() const { return no_reload_scope_depth_ > 0; }
+ bool IsInStoppedMutatorsScope() const {
+ return stopped_mutators_scope_depth_ > 0;
+ }
+
#define DEFINE_OFFSET_METHOD(type_name, member_name, expr, default_init_value) \
static intptr_t member_name##offset() { \
return OFFSET_OF(Thread, member_name); \
@@ -1008,6 +1012,7 @@
ApiLocalScope* api_reusable_scope_;
int32_t no_callback_scope_depth_;
intptr_t no_reload_scope_depth_ = 0;
+ intptr_t stopped_mutators_scope_depth_ = 0;
#if defined(DEBUG)
int32_t no_safepoint_scope_depth_;
#endif
@@ -1105,6 +1110,7 @@
friend class NoReloadScope;
friend class Simulator;
friend class StackZone;
+ friend class StoppedMutatorsScope;
friend class ThreadRegistry;
friend class CompilerState;
friend class compiler::target::Thread;
@@ -1173,6 +1179,28 @@
DISALLOW_COPY_AND_ASSIGN(NoReloadScope);
};
+class StoppedMutatorsScope : public ThreadStackResource {
+ public:
+ explicit StoppedMutatorsScope(Thread* thread)
+ : ThreadStackResource(thread), thread_(thread) {
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+ thread->stopped_mutators_scope_depth_++;
+ ASSERT(thread->stopped_mutators_scope_depth_ >= 0);
+#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+ }
+
+ ~StoppedMutatorsScope() {
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+ thread_->stopped_mutators_scope_depth_ -= 1;
+ ASSERT(thread_->stopped_mutators_scope_depth_ >= 0);
+#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+ }
+
+ private:
+ Thread* thread_;
+ DISALLOW_COPY_AND_ASSIGN(StoppedMutatorsScope);
+};
+
// Within a EnterCompilerScope, the thread must operate on cloned fields.
#if defined(DEBUG)
class EnterCompilerScope : public ThreadStackResource {
diff --git a/tests/language/generic/function_typedef2_test.dart b/tests/language/generic/function_typedef2_test.dart
index 6d9c0ff..22fd248 100644
--- a/tests/language/generic/function_typedef2_test.dart
+++ b/tests/language/generic/function_typedef2_test.dart
@@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
// Dart test for a function type test that cannot be eliminated at compile time.
+// This test validates the static errors for typedefs in language versions
+// prior to the release of nonfunction type aliases (Dart 2.13).
+// @dart=2.12
+
import "package:expect/expect.dart";
class A {}
diff --git a/tests/language/generic/function_typedef3_test.dart b/tests/language/generic/function_typedef3_test.dart
new file mode 100644
index 0000000..47c3838
--- /dev/null
+++ b/tests/language/generic/function_typedef3_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, 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.
+// Dart test for a function type test that cannot be eliminated at compile time.
+
+// This test validates the static errors for typedefs as per the code in
+// function_typedef2_test.dart in language versions after the release of
+// nonfunction type aliases (Dart 2.13).
+
+import "package:expect/expect.dart";
+
+class A {}
+
+typedef int F();
+
+typedef G = F;
+
+typedef H = int;
+
+typedef I = A;
+
+typedef J = List<int>;
+
+typedef K = Function(Function<A>(A<int>));
+// ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.WRONG_NUMBER_OF_TYPE_ARGUMENTS
+// [cfe] Can't use type arguments with type variable 'A'.
+typedef L = Function({x});
+// ^
+// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_CLASS
+// [cfe] Type 'x' not found.
+// ^
+// [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+// [cfe] Expected an identifier, but got '}'.
+
+typedef M = Function({int});
+ // ^
+ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+ // [cfe] Expected an identifier, but got '}'.
+
+foo({bool int = false}) {}
+main() {
+ bool b = true;
+ Expect.isFalse(b is L);
+ Expect.isFalse(b is M);
+ Expect.isTrue(foo is M);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 0ea8837..13bcfa2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 194
+PRERELEASE 195
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index f3375ea..336213d 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -119,9 +119,6 @@
variance:
help: "Sound variance"
- nonfunction-type-aliases:
- help: "Type aliases define a <type>, not just a <functionType>"
-
alternative-invalidation-strategy:
help: "Alternative invalidation strategy for incremental compilation"
category: "CFE"
@@ -143,6 +140,16 @@
# on the command line, and will eventually be removed.
#
+ nonfunction-type-aliases:
+ help: "Type aliases define a <type>, not just a <functionType>"
+ enabledIn: '2.13.0'
+ validation: |
+ typedef S = String;
+ void main() {
+ S s = 'feature enabled';
+ print(s);
+ }
+
non-nullable:
help: "Non Nullable by default"
experimentalReleaseVersion: '2.10.0'