Version 2.17.0-61.0.dev
Merge commit '6c10e05ba339aa4da4fc38e19fc21b450080b9cb' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
index 4d4da53..7659c35 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
@@ -135,12 +135,16 @@
}
/// Add constructor suggestions for the given class.
- void _addConstructorSuggestions(ClassElement classElem) {
- for (var constructor in classElem.constructors) {
+ void _addConstructorSuggestions(ClassElement element) {
+ if (element.isEnum) {
+ return;
+ }
+
+ for (var constructor in element.constructors) {
if (constructor.isPrivate) {
continue;
}
- if (classElem.isAbstract && !constructor.isFactory) {
+ if (element.isAbstract && !constructor.isFactory) {
continue;
}
builder.suggestConstructor(constructor, kind: kind, prefix: prefix);
diff --git a/pkg/analysis_server/test/client/completion_driver_test.dart b/pkg/analysis_server/test/client/completion_driver_test.dart
index a12950d..52c2abf 100644
--- a/pkg/analysis_server/test/client/completion_driver_test.dart
+++ b/pkg/analysis_server/test/client/completion_driver_test.dart
@@ -10,6 +10,7 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import '../services/completion/dart/completion_check.dart';
import '../services/completion/dart/completion_contributor_util.dart';
import 'impl/completion_driver.dart';
@@ -123,6 +124,21 @@
return suggestions;
}
+ /// TODO(scheglov) Use it everywhere instead of [addTestFile].
+ Future<CompletionResponseForTesting> getTestCodeSuggestions(
+ String content,
+ ) async {
+ await addTestFile(content);
+
+ return CompletionResponseForTesting(
+ requestOffset: driver.completionOffset,
+ replacementOffset: driver.replacementOffset,
+ replacementLength: driver.replacementLength,
+ isIncomplete: false, // TODO(scheglov) not correct
+ suggestions: suggestions,
+ );
+ }
+
/// Display sorted suggestions.
void printSuggestions() {
suggestions.sort(completionComparator);
diff --git a/pkg/analysis_server/test/client/impl/completion_driver.dart b/pkg/analysis_server/test/client/impl/completion_driver.dart
index 66720b9..4907915 100644
--- a/pkg/analysis_server/test/client/impl/completion_driver.dart
+++ b/pkg/analysis_server/test/client/impl/completion_driver.dart
@@ -141,6 +141,8 @@
).toRequest('0');
var response = await waitResponse(request);
var result = CompletionGetSuggestions2Result.fromResponse(response);
+ replacementOffset = result.replacementOffset;
+ replacementLength = result.replacementLength;
return result.suggestions;
}
diff --git a/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart b/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart
new file mode 100644
index 0000000..842ac90
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/declaration/enum_test.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer_utilities/check/check.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../client/completion_driver_test.dart';
+import '../completion_check.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(EnumTest1);
+ defineReflectiveTests(EnumTest2);
+ });
+}
+
+@reflectiveTest
+class EnumTest1 extends AbstractCompletionDriverTest with EnumTestCases {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
+}
+
+@reflectiveTest
+class EnumTest2 extends AbstractCompletionDriverTest with EnumTestCases {
+ @override
+ TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
+}
+
+mixin EnumTestCases on AbstractCompletionDriverTest {
+ @override
+ bool get supportsAvailableSuggestions => true;
+
+ Future<void> test_unprefixed_imported() async {
+ await addProjectFile('lib/a.dart', r'''
+enum MyEnum { v }
+''');
+
+ var response = await getTestCodeSuggestions('''
+import 'a.dart';
+
+void f() {
+ ^
+}
+''');
+
+ _checkUnprefixed(response);
+ }
+
+ Future<void> test_unprefixed_local() async {
+ var response = await getTestCodeSuggestions('''
+enum MyEnum { v }
+
+void f() {
+ ^
+}
+''');
+
+ _checkUnprefixed(response);
+ }
+
+ Future<void> test_unprefixed_notImported() async {
+ await addProjectFile('lib/a.dart', r'''
+enum MyEnum { v }
+''');
+
+ var response = await getTestCodeSuggestions('''
+void f() {
+ ^
+}
+''');
+
+ _checkUnprefixed(response);
+ }
+
+ void _checkUnprefixed(CompletionResponseForTesting response) {
+ check(response).suggestions
+ ..includesAll([
+ (suggestion) => suggestion.completion.isEqualTo('MyEnum.v'),
+ ])
+ ..excludesAll([
+ (suggestion) => suggestion
+ ..completion.startsWith('MyEnum')
+ ..isConstructorInvocation,
+ ]);
+ }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart b/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart
new file mode 100644
index 0000000..b1de9ef
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/dart/declaration/test_all.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'enum_test.dart' as enum_;
+
+/// Tests suggestions produced for various kinds of declarations.
+void main() {
+ defineReflectiveSuite(() {
+ enum_.main();
+ });
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart b/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart
index 8577738..0254e45 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart
@@ -10,7 +10,7 @@
void main() {
defineReflectiveSuite(() {
- // defineReflectiveTests(BoolAssignmentTest1);
+ defineReflectiveTests(BoolAssignmentTest1);
defineReflectiveTests(BoolAssignmentTest2);
});
}
diff --git a/pkg/analysis_server/test/services/completion/dart/test_all.dart b/pkg/analysis_server/test/services/completion/dart/test_all.dart
index 384422b..0ed0678 100644
--- a/pkg/analysis_server/test/services/completion/dart/test_all.dart
+++ b/pkg/analysis_server/test/services/completion/dart/test_all.dart
@@ -8,6 +8,7 @@
import 'closure_contributor_test.dart' as closure_contributor;
import 'combinator_contributor_test.dart' as combinator_test;
import 'completion_manager_test.dart' as completion_manager;
+import 'declaration/test_all.dart' as declaration;
import 'extension_member_contributor_test.dart' as extension_member_contributor;
import 'field_formal_contributor_test.dart' as field_formal_contributor_test;
import 'imported_reference_contributor_test.dart' as imported_ref_test;
@@ -32,6 +33,7 @@
closure_contributor.main();
combinator_test.main();
completion_manager.main();
+ declaration.main();
extension_member_contributor.main();
field_formal_contributor_test.main();
imported_ref_test.main();
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index d3dc572..edeba68 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -8431,35 +8431,38 @@
/**
* 12.1 Constants: A constant expression is ... a constant list literal.
+ *
+ * Note: This diagnostic is never displayed to the user, so it doesn't need
+ * to be documented.
*/
static const CompileTimeErrorCode MISSING_CONST_IN_LIST_LITERAL =
CompileTimeErrorCode(
'MISSING_CONST_IN_LIST_LITERAL',
- "List literals must be prefixed with 'const' when used as a constant "
- "expression.",
- correctionMessage: "Try adding the keyword 'const' before the literal.",
+ "Seeing this message constitutes a bug. Please report it.",
);
/**
* 12.1 Constants: A constant expression is ... a constant map literal.
+ *
+ * Note: This diagnostic is never displayed to the user, so it doesn't need
+ * to be documented.
*/
static const CompileTimeErrorCode MISSING_CONST_IN_MAP_LITERAL =
CompileTimeErrorCode(
'MISSING_CONST_IN_MAP_LITERAL',
- "Map literals must be prefixed with 'const' when used as a constant "
- "expression.",
- correctionMessage: "Try adding the keyword 'const' before the literal.",
+ "Seeing this message constitutes a bug. Please report it.",
);
/**
* 12.1 Constants: A constant expression is ... a constant set literal.
+ *
+ * Note: This diagnostic is never displayed to the user, so it doesn't need
+ * to be documented.
*/
static const CompileTimeErrorCode MISSING_CONST_IN_SET_LITERAL =
CompileTimeErrorCode(
'MISSING_CONST_IN_SET_LITERAL',
- "Set literals must be prefixed with 'const' when used as a constant "
- "expression.",
- correctionMessage: "Try adding the keyword 'const' before the literal.",
+ "Seeing this message constitutes a bug. Please report it.",
);
/**
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index f3dcc6d..d791d4c 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -7304,17 +7304,26 @@
var m = <String, int>{'a' : 2};
```
MISSING_CONST_IN_LIST_LITERAL:
- problemMessage: "List literals must be prefixed with 'const' when used as a constant expression."
- correctionMessage: "Try adding the keyword 'const' before the literal."
- comment: "12.1 Constants: A constant expression is ... a constant list literal."
+ problemMessage: Seeing this message constitutes a bug. Please report it.
+ comment: |-
+ 12.1 Constants: A constant expression is ... a constant list literal.
+
+ Note: This diagnostic is never displayed to the user, so it doesn't need
+ to be documented.
MISSING_CONST_IN_MAP_LITERAL:
- problemMessage: "Map literals must be prefixed with 'const' when used as a constant expression."
- correctionMessage: "Try adding the keyword 'const' before the literal."
- comment: "12.1 Constants: A constant expression is ... a constant map literal."
+ problemMessage: Seeing this message constitutes a bug. Please report it.
+ comment: |-
+ 12.1 Constants: A constant expression is ... a constant map literal.
+
+ Note: This diagnostic is never displayed to the user, so it doesn't need
+ to be documented.
MISSING_CONST_IN_SET_LITERAL:
- problemMessage: "Set literals must be prefixed with 'const' when used as a constant expression."
- correctionMessage: "Try adding the keyword 'const' before the literal."
- comment: "12.1 Constants: A constant expression is ... a constant set literal."
+ problemMessage: Seeing this message constitutes a bug. Please report it.
+ comment: |-
+ 12.1 Constants: A constant expression is ... a constant set literal.
+
+ Note: This diagnostic is never displayed to the user, so it doesn't need
+ to be documented.
MISSING_DART_LIBRARY:
problemMessage: "Required library '{0}' is missing."
correctionMessage: Re-install the Dart or Flutter SDK.
diff --git a/runtime/tests/vm/dart/splay_test.dart b/runtime/tests/vm/dart/splay_test.dart
index 7e974a1..1abb197 100644
--- a/runtime/tests/vm/dart/splay_test.dart
+++ b/runtime/tests/vm/dart/splay_test.dart
@@ -27,6 +27,7 @@
// VMOptions=--verify_store_buffer
// VMOptions=--stress_write_barrier_elimination
// VMOptions=--old_gen_heap_size=100
+// VMOptions=--mark_when_idle
import "dart:math";
import 'package:benchmark_harness/benchmark_harness.dart';
diff --git a/runtime/tests/vm/dart_2/splay_test.dart b/runtime/tests/vm/dart_2/splay_test.dart
index a793419..f1b401e 100644
--- a/runtime/tests/vm/dart_2/splay_test.dart
+++ b/runtime/tests/vm/dart_2/splay_test.dart
@@ -31,6 +31,7 @@
// VMOptions=--verify_store_buffer
// VMOptions=--stress_write_barrier_elimination
// VMOptions=--old_gen_heap_size=100
+// VMOptions=--mark_when_idle
import "dart:math";
import 'package:benchmark_harness/benchmark_harness.dart';
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index b10e2d7..244a95e 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -4027,7 +4027,7 @@
}
__ SmiUntag(TMP2, left);
__ srl(TMP, TMP2, TMP);
- __ SmiTag(result);
+ __ SmiTag(result, TMP);
if (deopt != nullptr) {
__ SmiUntag(TMP2, result);
__ bne(TMP, TMP2, deopt);
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 3e0ea0f..b5cde40 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -140,6 +140,9 @@
P(scavenger_tasks, int, 2, \
"The number of tasks to spawn during scavenging (0 means " \
"perform all marking on main thread).") \
+ P(mark_when_idle, bool, false, \
+ "The Dart thread will assist in concurrent marking during idle time and " \
+ "is counted as one marker task") \
P(marker_tasks, int, 2, \
"The number of tasks to spawn during old gen GC marking (0 means " \
"perform all marking on main thread).") \
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index b226659..286206f 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -243,6 +243,7 @@
#endif
while ((old_space_->tasks() > 0) ||
(old_space_->phase() != PageSpace::kDone)) {
+ old_space_->AssistTasks(&ml);
if (old_space_->phase() == PageSpace::kAwaitingFinalization) {
ml.Exit();
heap_->CollectOldSpaceGarbage(thread, GCType::kMarkSweep,
@@ -408,6 +409,8 @@
}
}
+ old_space_.NotifyIdle(deadline);
+
if (OS::GetCurrentMonotonicMicros() < deadline) {
SemiSpace::DrainCache();
}
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index 43b4c62..adbaab4 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -78,15 +78,37 @@
}
void DrainMarkingStack() {
+ while (ProcessMarkingStack()) {
+ }
+ }
+
+ void ProcessMarkingStackUntil(int64_t deadline) {
+ // We check the clock *before* starting a batch of work, but we want to
+ // *end* work before the deadline. So we compare to the deadline adjusted
+ // by a conservative estimate of the duration of one batch of work.
+ deadline -= 1500;
+
+ while ((OS::GetCurrentMonotonicMicros() < deadline) &&
+ ProcessMarkingStack()) {
+ }
+ }
+
+ bool ProcessMarkingStack() {
ObjectPtr raw_obj = work_list_.Pop();
if ((raw_obj == nullptr) && ProcessPendingWeakProperties()) {
raw_obj = work_list_.Pop();
}
if (raw_obj == nullptr) {
- return;
+ return false; // No more work.
}
+ // A 512kB budget is choosen to be large enough that we don't waste too much
+ // time on the overhead of exiting this function, querying the clock, and
+ // re-entering, and small enough that a few batches can fit in the idle time
+ // between animation frames. This amount of marking takes ~1ms on a Pixel
+ // phone.
+ intptr_t remaining_budget = 512 * KB;
do {
do {
// First drain the marking stacks.
@@ -100,6 +122,10 @@
size = ProcessWeakProperty(raw_weak, /* did_mark */ true);
}
marked_bytes_ += size;
+ remaining_budget -= size;
+ if (remaining_budget < 0) {
+ return true; // More to mark.
+ }
raw_obj = work_list_.Pop();
} while (raw_obj != nullptr);
@@ -111,6 +137,8 @@
// by the handling of weak properties.
raw_obj = work_list_.Pop();
} while (raw_obj != nullptr);
+
+ return false; // No more work.
}
// Races: The concurrent marker is racing with the mutator, but this race is
@@ -767,6 +795,9 @@
&deferred_marking_stack_);
const intptr_t num_tasks = FLAG_marker_tasks;
+ RELEASE_ASSERT(num_tasks >= 1);
+ const intptr_t num_concurrent_tasks =
+ num_tasks - (FLAG_mark_when_idle ? 1 : 0);
{
// Bulk increase task count before starting any task, instead of
@@ -775,9 +806,9 @@
MonitorLocker ml(page_space->tasks_lock());
ASSERT(page_space->phase() == PageSpace::kDone);
page_space->set_phase(PageSpace::kMarking);
- page_space->set_tasks(page_space->tasks() + num_tasks);
+ page_space->set_tasks(page_space->tasks() + num_concurrent_tasks);
page_space->set_concurrent_marker_tasks(
- page_space->concurrent_marker_tasks() + num_tasks);
+ page_space->concurrent_marker_tasks() + num_concurrent_tasks);
}
ResetSlices();
@@ -793,7 +824,7 @@
this, isolate_group_, page_space, visitor);
ASSERT(result);
} else {
- // Last worker is the main thread, which will only mark roots.
+ // For the last visitor, mark roots on the main thread.
TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "ConcurrentMark");
int64_t start = OS::GetCurrentMonotonicMicros();
IterateRoots(visitor);
@@ -803,10 +834,16 @@
THR_Print("Task marked %" Pd " bytes in %" Pd64 " micros.\n",
visitor->marked_bytes(), visitor->marked_micros());
}
- // Continue non-root marking concurrently.
- bool result = Dart::thread_pool()->Run<ConcurrentMarkTask>(
- this, isolate_group_, page_space, visitor);
- ASSERT(result);
+ if (FLAG_mark_when_idle) {
+ // Not spawning a thread to continue processing with the last visitor.
+ // This visitor is instead left available for the main thread to
+ // contribute to marking during idle time.
+ } else {
+ // Continue non-root marking concurrently.
+ bool result = Dart::thread_pool()->Run<ConcurrentMarkTask>(
+ this, isolate_group_, page_space, visitor);
+ ASSERT(result);
+ }
}
}
@@ -819,6 +856,31 @@
}
}
+void GCMarker::AssistConcurrentMark() {
+ if (!FLAG_mark_when_idle) return;
+
+ SyncMarkingVisitor* visitor = visitors_[FLAG_marker_tasks - 1];
+ ASSERT(visitor != nullptr);
+ TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "Mark");
+ int64_t start = OS::GetCurrentMonotonicMicros();
+ visitor->DrainMarkingStack();
+ int64_t stop = OS::GetCurrentMonotonicMicros();
+ visitor->AddMicros(stop - start);
+}
+
+void GCMarker::NotifyIdle(int64_t deadline) {
+ if (!FLAG_mark_when_idle) return;
+
+ SyncMarkingVisitor* visitor = visitors_[FLAG_marker_tasks - 1];
+ if (visitor == nullptr) return;
+
+ TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "IncrementalMark");
+ int64_t start = OS::GetCurrentMonotonicMicros();
+ visitor->ProcessMarkingStackUntil(deadline);
+ int64_t stop = OS::GetCurrentMonotonicMicros();
+ visitor->AddMicros(stop - start);
+}
+
void GCMarker::MarkObjects(PageSpace* page_space) {
if (isolate_group_->marking_stack() != NULL) {
isolate_group_->DisableIncrementalBarrier();
diff --git a/runtime/vm/heap/marker.h b/runtime/vm/heap/marker.h
index 2fde0d8..ec36170 100644
--- a/runtime/vm/heap/marker.h
+++ b/runtime/vm/heap/marker.h
@@ -38,6 +38,13 @@
// Marking must later be finalized by calling MarkObjects.
void StartConcurrentMark(PageSpace* page_space);
+ // Called when a synchronous GC is required, but concurrent marking is still
+ // in progress.
+ void AssistConcurrentMark();
+
+ // Perform incremental marking if available.
+ void NotifyIdle(int64_t deadline);
+
// (Re)mark roots, drain the marking queue and finalize weak references.
// Does not required StartConcurrentMark to have been previously called.
void MarkObjects(PageSpace* page_space);
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index f10cdd7..440bcd0 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1036,6 +1036,20 @@
return estimated_mark_compact_completion <= deadline;
}
+void PageSpace::NotifyIdle(int64_t deadline) {
+ if (marker_ != nullptr) {
+ marker_->NotifyIdle(deadline);
+ }
+}
+
+void PageSpace::AssistTasks(MonitorLocker* ml) {
+ if (phase() == PageSpace::kMarking) {
+ ml->Exit();
+ marker_->AssistConcurrentMark();
+ ml->Enter();
+ }
+}
+
void PageSpace::TryReleaseReservation() {
ASSERT(phase() != kSweepingLarge);
ASSERT(phase() != kSweepingRegular);
@@ -1107,6 +1121,7 @@
return;
}
+ AssistTasks(&locker);
while (tasks() > 0) {
locker.Wait();
}
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 11a4cc2..5a3c5c0 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -434,6 +434,8 @@
bool ShouldStartIdleMarkSweep(int64_t deadline);
bool ShouldPerformIdleMarkCompact(int64_t deadline);
+ void NotifyIdle(int64_t deadline);
+ void AssistTasks(MonitorLocker* ml);
void AddGCTime(int64_t micros) { gc_time_micros_ += micros; }
diff --git a/tests/language/vm/fuzzer_unsigned_shift_right_test.dart b/tests/language/vm/fuzzer_unsigned_shift_right_test.dart
new file mode 100644
index 0000000..01e935a
--- /dev/null
+++ b/tests/language/vm/fuzzer_unsigned_shift_right_test.dart
@@ -0,0 +1,26 @@
+// 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=--deterministic
+
+// The Dart Project Fuzz Tester (1.93).
+// Program generated as:
+// dart dartfuzz.dart --seed 316265767 --no-fp --no-ffi --flat
+
+import 'dart:typed_data';
+
+Int16List? foo0_0(int par4) {
+ if (par4 >= 36) {
+ return Int16List(40);
+ }
+ for (int loc0 = 0; loc0 < 31; loc0++) {
+ for (int loc1 in ((Uint8ClampedList.fromList(Uint8List(26)))
+ .sublist((11 >>> loc0), null))) {}
+ }
+ return foo0_0(par4 + 1);
+}
+
+main() {
+ foo0_0(0);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 206390c..020f99a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 60
+PRERELEASE 61
PRERELEASE_PATCH 0
\ No newline at end of file