Version 3.5.0-97.0.dev
Merge 6118b2f5904877e48dbd740e1fafae1711c9b56f into dev
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 478a0b5..1efa637 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -414,10 +414,11 @@
}) {
var element = suggestion.element;
var parameters = element?.parameters;
- // Prefer the element return type (because it's available for things like
- // overrides) but fall back to the suggestion if there isn't one (to handle
- // records).
- var returnType = element?.returnType ?? suggestion.returnType;
+ // Prefer the element return type (because it may be more specific
+ // for overrides) and fall back to the parameter type or return type from the
+ // suggestion (handles records).
+ var returnType =
+ element?.returnType ?? suggestion.parameterType ?? suggestion.returnType;
// Extract the type from setters to be shown in the place a return type
// would usually be shown.
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 50b5662..8d2c795 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -891,10 +891,11 @@
parameterName: name,
parameterType: type,
replacementLength: replacementLength,
+ element: convertElement(parameter),
elementLocation: parameter.location);
+
if (parameter is FieldFormalParameterElement) {
_setDocumentation(suggestion, parameter);
- suggestion.element = convertElement(parameter);
}
_addSuggestion(suggestion);
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index bdce7a6..8a0d21f 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -4,7 +4,6 @@
import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
import 'package:analysis_server/plugin/edit/assist/assist_dart.dart';
-import 'package:analysis_server/src/services/correction/base_processor.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
import 'package:analysis_server/src/services/correction/dart/add_return_type.dart';
@@ -74,6 +73,7 @@
import 'package:analysis_server/src/services/correction/dart/surround_with.dart';
import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart';
import 'package:analysis_server/src/services/correction/fix_processor.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/util/file_paths.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart'
@@ -82,7 +82,7 @@
import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
/// The computer for Dart assists.
-class AssistProcessor extends BaseProcessor {
+class AssistProcessor {
/// A list of the generators used to produce assists.
static const List<ProducerGenerator> generators = [
AddDiagnosticPropertyReference.new,
@@ -164,13 +164,7 @@
final List<Assist> assists = <Assist>[];
- AssistProcessor(this.assistContext)
- : super(
- selectionOffset: assistContext.selectionOffset,
- selectionLength: assistContext.selectionLength,
- resolvedResult: assistContext.resolveResult,
- workspace: assistContext.workspace,
- );
+ AssistProcessor(this.assistContext);
Future<List<Assist>> compute() async {
if (isMacroGenerated(assistContext.resolveResult.file.path)) {
@@ -193,10 +187,10 @@
Future<void> _addFromProducers() async {
var context = CorrectionProducerContext.createResolved(
- selectionOffset: selectionOffset,
- selectionLength: selectionLength,
- resolvedResult: resolvedResult,
- workspace: workspace,
+ selectionOffset: assistContext.selectionOffset,
+ selectionLength: assistContext.selectionLength,
+ resolvedResult: assistContext.resolveResult,
+ workspace: assistContext.workspace,
);
if (context == null) {
return;
@@ -245,7 +239,10 @@
ProducerGenerator generator,
Set<String> errorCodes,
) {
- var node = findSelectedNode();
+ var selectionEnd =
+ assistContext.selectionOffset + assistContext.selectionLength;
+ var locator = NodeLocator(assistContext.selectionOffset, selectionEnd);
+ var node = locator.searchWithin(assistContext.resolveResult.unit);
if (node == null) {
return false;
}
@@ -253,7 +250,7 @@
var fileOffset = node.offset;
for (var error in assistContext.resolveResult.errors) {
var errorSource = error.source;
- if (file == errorSource.fullName) {
+ if (assistContext.resolveResult.path == errorSource.fullName) {
if (fileOffset >= error.offset &&
fileOffset <= error.offset + error.length) {
if (errorCodes.contains(error.errorCode.name)) {
diff --git a/pkg/analysis_server/lib/src/services/correction/base_processor.dart b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
deleted file mode 100644
index 7fcff630..0000000
--- a/pkg/analysis_server/lib/src/services/correction/base_processor.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2019, 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:analysis_server_plugin/edit/correction_utils.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/element/type_provider.dart';
-import 'package:analyzer/src/dart/analysis/session_helper.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
-import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
-
-/// Base class for common processor functionality.
-abstract class BaseProcessor {
- final int selectionOffset;
- final int selectionLength;
- final int selectionEnd;
-
- final CorrectionUtils utils;
- final String file;
-
- final TypeProvider typeProvider;
-
- final AnalysisSession session;
- final AnalysisSessionHelper sessionHelper;
- final ResolvedUnitResult resolvedResult;
- final ChangeWorkspace workspace;
-
- BaseProcessor({
- this.selectionOffset = -1,
- this.selectionLength = 0,
- required this.resolvedResult,
- required this.workspace,
- }) : file = resolvedResult.path,
- session = resolvedResult.session,
- sessionHelper = AnalysisSessionHelper(resolvedResult.session),
- typeProvider = resolvedResult.typeProvider,
- selectionEnd = selectionOffset + selectionLength,
- utils = CorrectionUtils(resolvedResult);
-
- AstNode? findSelectedNode() {
- var locator = NodeLocator(selectionOffset, selectionEnd);
- return locator.searchWithin(resolvedResult.unit);
- }
-}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/fix_processor.dart
index 139e64e..c1090ca 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_processor.dart
@@ -2,7 +2,6 @@
// 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:analysis_server/src/services/correction/base_processor.dart';
import 'package:analysis_server/src/services/correction/bulk_fix_processor.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/dart/ignore_diagnostic.dart';
@@ -23,7 +22,7 @@
typedef ProducerGenerator = CorrectionProducer Function();
/// The computer for Dart fixes.
-class FixProcessor extends BaseProcessor {
+class FixProcessor {
/// Cached results of [canBulkFix].
static final Map<ErrorCode, bool> _bulkFixableErrorCodes = {};
@@ -62,11 +61,7 @@
final List<Fix> fixes = <Fix>[];
- FixProcessor(this.fixContext)
- : super(
- resolvedResult: fixContext.resolvedResult,
- workspace: fixContext.workspace,
- );
+ FixProcessor(this.fixContext);
Future<List<Fix>> compute() async {
if (isMacroGenerated(fixContext.resolvedResult.file.path)) {
@@ -105,10 +100,10 @@
var context = CorrectionProducerContext.createResolved(
dartFixContext: fixContext,
diagnostic: error,
- resolvedResult: resolvedResult,
+ resolvedResult: fixContext.resolvedResult,
selectionOffset: fixContext.error.offset,
selectionLength: fixContext.error.length,
- workspace: workspace,
+ workspace: fixContext.workspace,
);
if (context == null) {
return;
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index a9fffca..4eb2bcd 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -310,6 +310,47 @@
setCompletionItemLabelDetailsSupport();
}
+ Future<void> test_constructor_argument() async {
+ var content = '''
+var a = Foo(^);
+
+class Foo {
+ final int value;
+ const Foo({required this.value});
+}
+''';
+
+ await expectLabels(
+ content,
+ label: 'value:',
+ labelDetail: ' int',
+ labelDescription: null,
+ filterText: null,
+ detail: 'int',
+ );
+ }
+
+ Future<void> test_constructor_factory_argument() async {
+ var content = '''
+var a = Foo(^);
+
+class Foo {
+ final int value;
+ const Foo._({required this.value});
+ const factory Foo({required int value}) = Foo._;
+}
+''';
+
+ await expectLabels(
+ content,
+ label: 'value:',
+ labelDetail: ' int',
+ labelDescription: null,
+ filterText: null,
+ detail: 'int',
+ );
+ }
+
Future<void> test_imported_function_returnType_args() async {
newFile(fileAPath, '''
String a(String a, {String b}) {}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/argument_list_test.dart b/pkg/analysis_server/test/services/completion/dart/location/argument_list_test.dart
index e455265..1f4577b 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/argument_list_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/argument_list_test.dart
@@ -79,6 +79,32 @@
''');
}
+ Future<void> test_afterLeftParen_beforeRightParen_factoryConstructor() async {
+ printerConfiguration
+ ..withDocumentation = true
+ ..withElement = true;
+
+ await computeSuggestions('''
+class A {
+ int fff;
+ A._({this.fff});
+ factory A({int fff}) = A._;
+}
+void f() {
+ new A(^);
+}
+''');
+
+ assertResponse(r'''
+suggestions
+ |fff: |
+ kind: namedArgument
+ element
+ name: fff
+ kind: parameter
+''');
+ }
+
Future<void>
test_afterLeftParen_beforeRightParen_fieldFormal_withDocumentation() async {
printerConfiguration
diff --git a/pkg/compiler/lib/src/serialization/indexed_sink_source.dart b/pkg/compiler/lib/src/serialization/indexed_sink_source.dart
index 9af89ea..927580a 100644
--- a/pkg/compiler/lib/src/serialization/indexed_sink_source.dart
+++ b/pkg/compiler/lib/src/serialization/indexed_sink_source.dart
@@ -252,7 +252,12 @@
} else if (markerOrOffset == _nullIndicator) {
return null;
} else {
- final offset = markerOrOffset - _indicatorOffset;
+ int offset;
+ if (markerOrOffset == _nonCompactOffsetIndicator) {
+ offset = source.readUint32();
+ } else {
+ offset = markerOrOffset - _indicatorOffset;
+ }
bool isLocal = _isLocalOffset(offset);
final globalOffset =
isLocal ? _localToGlobalOffset(offset, source) : offset;
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index bf1609f..5ca229a 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 4.2.1
+- [DAP]: For consistency with other values, automatic `toString()` invocations for debugger views no longer expand long strings and instead show truncated values. Full values continue to be returned for evaluation (`context=="repl"`) and when copying to the clipboard (`context=="clipboard"`).
+
# 4.2.0
- [DAP] All `OutputEvent`s are now scanned for stack frames to attach `source` metadata to. The [parseStackFrames] parameter for `sendOutput` is ignored and deprecated.
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index fe7d233..a998284 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -2265,6 +2265,13 @@
final lineEnd = i != lines.length - 1 ? '\n' : '';
final output = '$linePrefix$line$lineSuffix$lineEnd';
+ // If the output is empty (for example the output ended with \n so after
+ // splitting by \n, the last iteration is empty) then we don't need
+ // to add any event.
+ if (output.isEmpty) {
+ continue;
+ }
+
final clientPath =
fileLikeUri != null ? toClientPathOrUri(fileLikeUri) : null;
events.add(
diff --git a/pkg/dds/lib/src/dap/protocol_converter.dart b/pkg/dds/lib/src/dap/protocol_converter.dart
index 1b6ae7a..98ec3b3 100644
--- a/pkg/dds/lib/src/dap/protocol_converter.dart
+++ b/pkg/dds/lib/src/dap/protocol_converter.dart
@@ -69,6 +69,7 @@
var stringValue = allowCallingToString &&
(valueAsString == null || !allowTruncatedValue)
? await _callToString(thread, ref,
+ allowTruncatedValue: allowTruncatedValue,
// Quotes are handled below, so they can be wrapped around the
// ellipsis.
format: VariableFormat.noQuotes())
@@ -91,6 +92,7 @@
var stringValue = ref.classRef?.name ?? '<unknown instance>';
if (allowCallingToString) {
final toStringValue = await _callToString(thread, ref,
+ allowTruncatedValue: allowTruncatedValue,
// Suppress quotes because this is going inside a longer string.
format: VariableFormat.noQuotes());
// Include the toString() result only if it's not the default (which
@@ -696,6 +698,7 @@
Future<String?> _callToString(
ThreadInfo thread,
vm.InstanceRef ref, {
+ bool allowTruncatedValue = true,
VariableFormat? format,
}) async {
final service = _adapter.vmService;
@@ -714,7 +717,8 @@
// full value.
if (result is vm.InstanceRef &&
result.kind == 'String' &&
- (result.valueAsStringIsTruncated ?? false)) {
+ (result.valueAsStringIsTruncated ?? false) &&
+ !allowTruncatedValue) {
result = await service.getObject(thread.isolate.id!, result.id!);
}
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index 3344caf..ddb1509 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -1,5 +1,5 @@
name: dds
-version: 4.2.0
+version: 4.2.1
description: >-
A library used to spawn the Dart Developer Service, used to communicate with
a Dart VM Service instance.
diff --git a/pkg/dds/test/dap/integration/debug_test.dart b/pkg/dds/test/dap/integration/debug_test.dart
index d775541..33f3ea9 100644
--- a/pkg/dds/test/dap/integration/debug_test.dart
+++ b/pkg/dds/test/dap/integration/debug_test.dart
@@ -50,6 +50,27 @@
]);
});
+ test('does not include empty output events when output ends with a newline',
+ () async {
+ final testFile = dap.createTestFile(simpleArgPrintingProgram);
+
+ final outputEvents = await dap.client.collectOutput(
+ launch: () => dap.client.launch(
+ testFile.path,
+ args: ['one', 'two'],
+ ),
+ );
+
+ // The sample application uses `print()` which includes newlines on
+ // each output. Output is split by `\n` when scanning for stack frames
+ // and previously would include empty output events at the end if the
+ // content ended with a newline.
+ // https://github.com/flutter/flutter/pull/147250#issuecomment-2075128834
+ for (var output in outputEvents) {
+ expect(output.output, isNotEmpty);
+ }
+ });
+
test('runs a simple script using runInTerminal request', () async {
final testFile = dap.createTestFile(emptyProgram);
diff --git a/pkg/dds/test/dap/integration/debug_variables_test.dart b/pkg/dds/test/dap/integration/debug_variables_test.dart
index 9b7a982..4d2f5a8 100644
--- a/pkg/dds/test/dap/integration/debug_variables_test.dart
+++ b/pkg/dds/test/dap/integration/debug_variables_test.dart
@@ -700,6 +700,38 @@
);
});
+ test('uses truncated values from calling toString()', () async {
+ final client = dap.client;
+ final testFile = dap.createTestFile('''
+class Foo {
+ toString() => 'a' * 500 + 'b' * 500;
+}
+
+void main() {
+ final myVariable = Foo();
+ print('Hello!'); $breakpointMarker
+}
+ ''');
+ final breakpointLine = lineWith(testFile, breakpointMarker);
+
+ final stop = await client.hitBreakpoint(
+ testFile,
+ breakpointLine,
+ launch: () => client.launch(
+ testFile.path,
+ evaluateToStringInDebugViews: true,
+ ),
+ );
+
+ await client.expectScopeVariables(
+ await client.getTopFrameId(stop.threadId!),
+ 'Locals',
+ '''
+ myVariable: Foo (${'a' * 128}…), eval: myVariable
+ ''',
+ );
+ });
+
test('handles errors in toString() on custom classes', () async {
final client = dap.client;
final testFile = dap.createTestFile('''
diff --git a/pkg/dds/test/dap/integration/test_scripts.dart b/pkg/dds/test/dap/integration/test_scripts.dart
index 9b99f6b..5e4e2e9 100644
--- a/pkg/dds/test/dap/integration/test_scripts.dart
+++ b/pkg/dds/test/dap/integration/test_scripts.dart
@@ -166,7 +166,6 @@
/// A simple script that provides a @withHello macro that adds a
/// `hello()` method to a class that prints "Hello".
const withHelloMacroImplementation = '''
-// There is no public API exposed yet, the in-progress API lives here.
import 'package:macros/macros.dart';
macro class WithHello implements ClassDeclarationsMacro {
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index d43ab2f..b72d1ed 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -4554,21 +4554,23 @@
void CheckStackOverflowElimination::EliminateStackOverflow(FlowGraph* graph) {
const bool should_remove_all = IsMarkedWithNoInterrupts(graph->function());
+ if (should_remove_all) {
+ for (auto entry : graph->reverse_postorder()) {
+ for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
+ if (it.Current()->IsCheckStackOverflow()) {
+ it.RemoveCurrentFromGraph();
+ }
+ }
+ }
+ return;
+ }
CheckStackOverflowInstr* first_stack_overflow_instr = nullptr;
- for (BlockIterator block_it = graph->reverse_postorder_iterator();
- !block_it.Done(); block_it.Advance()) {
- BlockEntryInstr* entry = block_it.Current();
-
+ for (auto entry : graph->reverse_postorder()) {
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
if (CheckStackOverflowInstr* instr = current->AsCheckStackOverflow()) {
- if (should_remove_all) {
- it.RemoveCurrentFromGraph();
- continue;
- }
-
if (first_stack_overflow_instr == nullptr) {
first_stack_overflow_instr = instr;
ASSERT(!first_stack_overflow_instr->in_loop());
diff --git a/runtime/vm/compiler/backend/redundancy_elimination_test.cc b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
index cf3fcf7..fb2879f 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination_test.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
@@ -1571,10 +1571,23 @@
ISOLATE_UNIT_TEST_CASE(CheckStackOverflowElimination_NoInterruptsPragma) {
const char* kScript = R"(
- @pragma('vm:unsafe:no-interrupts')
- void test() {
- for (int i = 0; i < 10; i++) {
+ @pragma('vm:prefer-inline')
+ int bar(int n) {
+ print(''); // Side-effectful operation
+ var sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += i;
}
+ return sum;
+ }
+
+ @pragma('vm:unsafe:no-interrupts')
+ int test() {
+ int result = 0;
+ for (int i = 0; i < 10; i++) {
+ result ^= bar(i);
+ }
+ return result;
}
)";
diff --git a/runtime/vm/json_test.cc b/runtime/vm/json_test.cc
index e01bc70..0a67ffe9 100644
--- a/runtime/vm/json_test.cc
+++ b/runtime/vm/json_test.cc
@@ -188,21 +188,23 @@
ElideJSONSubstring("classes", json_str, buffer);
ElideJSONSubstring("libraries", buffer, buffer);
ElideJSONSubstring("objects", buffer, buffer);
+ ElideJSONSubstring("line", buffer, buffer);
+ ElideJSONSubstring("column", buffer, buffer);
+ StripTokenPositions(buffer);
+
EXPECT_STREQ(
"[{\"type\":\"@Instance\",\"_vmType\":\"null\",\"class\":{\"type\":\"@"
"Class\",\"fixedId\":true,\"id\":\"\",\"name\":\"Null\",\"location\":{"
"\"type\":\"SourceLocation\",\"script\":{\"type\":\"@Script\","
- "\"fixedId\":true,\"id\":\"\",\"uri\":\"dart:core\\/null.dart\",\"_"
- "kind\":\"kernel\"},\"tokenPos\":925,\"endTokenPos\":1171,\"line\":23,"
- "\"column\":1},\"library\":{"
+ "\"fixedId\":true,\"id\":\"\",\"uri\":\"dart:core\\/null.dart\","
+ "\"_kind\":\"kernel\"}},\"library\":{"
"\"type\":\"@Library\",\"fixedId\":true,\"id\":\"\",\"name\":\"dart."
"core\",\"uri\":\"dart:core\"}},\"kind\":\"Null\",\"fixedId\":true,"
"\"id\":\"\",\"valueAsString\":\"null\"},{\"object_key\":{\"type\":\"@"
"Instance\",\"_vmType\":\"null\",\"class\":{\"type\":\"@Class\","
"\"fixedId\":true,\"id\":\"\",\"name\":\"Null\",\"location\":{\"type\":"
"\"SourceLocation\",\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
- "\"id\":\"\",\"uri\":\"dart:core\\/null.dart\",\"_kind\":\"kernel\"},"
- "\"tokenPos\":925,\"endTokenPos\":1171,\"line\":23,\"column\":1},"
+ "\"id\":\"\",\"uri\":\"dart:core\\/null.dart\",\"_kind\":\"kernel\"}},"
"\"library\":{\"type\":\"@"
"Library\",\"fixedId\":true,\"id\":\"\",\"name\":\"dart.core\",\"uri\":"
"\"dart:core\"}},\"kind\":\"Null\",\"fixedId\":true,\"id\":\"\","
diff --git a/tools/VERSION b/tools/VERSION
index 1e5afb11..5d64796 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 3
MINOR 5
PATCH 0
-PRERELEASE 96
+PRERELEASE 97
PRERELEASE_PATCH 0