[vm] Add option for GC at [re]throw.
Add a reduced test for crash / false ConcurrentModificationError observed by the isolate stress test.
TEST=ci
Bug: https://github.com/dart-lang/sdk/issues/60017
Change-Id: I665a7ec48c150d270e751ab13393384613d44674
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/408201
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
diff --git a/runtime/tests/vm/dart/nested_try_throw_environment_test.dart b/runtime/tests/vm/dart/nested_try_throw_environment_test.dart
new file mode 100644
index 0000000..83d2d0a
--- /dev/null
+++ b/runtime/tests/vm/dart/nested_try_throw_environment_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2025, 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=--gc_at_throw --force_evacuation
+
+import 'package:expect/expect.dart';
+import 'dart:async';
+
+controlFlow() {
+ for (final FutureOr<T> Function<T>(T) func in <dynamic>[id, future]) {
+ try {
+ try {
+ throw "string";
+ } catch (e) {
+ Expect.equals("string", e);
+ rethrow;
+ } finally {
+ Expect.equals(0, func(0));
+ }
+ } catch (e) {
+ Expect.equals("string", e);
+ } finally {
+ Expect.equals(0, func(0));
+ }
+ }
+}
+
+FutureOr<T> future<T>(T value) => value;
+FutureOr<T> id<T>(T value) => value;
+
+main() {
+ for (int i = 0; i < 100; i++) {
+ controlFlow();
+ }
+}
diff --git a/runtime/tools/dartfuzz/flag_fuzzer.dart b/runtime/tools/dartfuzz/flag_fuzzer.dart
index 7708b7e..2d046be 100644
--- a/runtime/tools/dartfuzz/flag_fuzzer.dart
+++ b/runtime/tools/dartfuzz/flag_fuzzer.dart
@@ -52,6 +52,7 @@
"--verify_before_gc",
"--verify_store_buffer",
"--write_protect_code",
+ "--gc_at_throw",
];
final compilerFlags = [
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index d581acd..b5df184 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -111,6 +111,7 @@
verbose_stack_overflow,
false,
"Print additional details about stack overflow.");
+DEFINE_FLAG(bool, gc_at_throw, false, "Run evacuating GC at throw and rethrow");
DECLARE_FLAG(int, reload_every);
DECLARE_FLAG(bool, reload_every_optimized);
@@ -1587,11 +1588,25 @@
}
DEFINE_RUNTIME_ENTRY(Throw, 1) {
+ if (FLAG_gc_at_throw) {
+ isolate->group()->heap()->CollectGarbage(thread, GCType::kEvacuate,
+ GCReason::kDebugging);
+ isolate->group()->heap()->CollectAllGarbage(GCReason::kDebugging,
+ /*compact=*/true);
+ }
+
const Instance& exception = Instance::CheckedHandle(zone, arguments.ArgAt(0));
Exceptions::Throw(thread, exception);
}
DEFINE_RUNTIME_ENTRY(ReThrow, 3) {
+ if (FLAG_gc_at_throw) {
+ isolate->group()->heap()->CollectGarbage(thread, GCType::kEvacuate,
+ GCReason::kDebugging);
+ isolate->group()->heap()->CollectAllGarbage(GCReason::kDebugging,
+ /*compact=*/true);
+ }
+
const Instance& exception = Instance::CheckedHandle(zone, arguments.ArgAt(0));
const Instance& stacktrace =
Instance::CheckedHandle(zone, arguments.ArgAt(1));