[vm/sdk] Use FinalThreadLocal for Iterable toString.

This allows use of toString in isolategroup-bound callbacks.

BUG=https://github.com/dart-lang/sdk/issues/61030.
TEST=ci
CoreLibraryReviewExempt: effectively vm-only change
Change-Id: Iccdc218716cbbe2346aa85bb6134f4129debe057
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/451841
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
This commit is contained in:
Alexander Aprelev
2025-12-11 09:32:41 -08:00
committed by Commit Queue
parent aae871a607
commit 2c715e92b6
12 changed files with 51 additions and 16 deletions
+4 -4
View File
@@ -27,7 +27,7 @@
(i32.const 0)
(global.get $global0)
(ref.func $"foo tear-off trampoline")
(ref.func $"foo tear-off trampoline_299")
(ref.func $"foo tear-off trampoline_300")
(struct.new $#Vtable-0-2)
(i32.const 12)
(i32.const 0)
@@ -52,7 +52,7 @@
(i32.const 0)
(global.get $global0)
(ref.func $"bar tear-off trampoline")
(ref.func $"bar tear-off trampoline_302")
(ref.func $"bar tear-off trampoline_303")
(struct.new $#Vtable-0-2)
(i32.const 12)
(i32.const 0)
@@ -74,7 +74,7 @@
(struct.new $#Closure-0-2))
(global $global0 (ref $#DummyStruct) <...>)
(func $bar tear-off trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_302 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_303 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline_299 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline_300 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
)
@@ -27,7 +27,7 @@
(i32.const 0)
(global.get $global0)
(ref.func $"foo tear-off trampoline")
(ref.func $"foo tear-off trampoline_299")
(ref.func $"foo tear-off trampoline_300")
(struct.new $#Vtable-0-2)
(i32.const 12)
(i32.const 0)
@@ -52,7 +52,7 @@
(i32.const 0)
(global.get $global0)
(ref.func $"bar tear-off trampoline")
(ref.func $"bar tear-off trampoline_304")
(ref.func $"bar tear-off trampoline_305")
(struct.new $#Vtable-0-2)
(i32.const 12)
(i32.const 0)
@@ -74,7 +74,7 @@
(struct.new $#Closure-0-2))
(global $global0 (ref $#DummyStruct) <...>)
(func $bar tear-off trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_304 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_305 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline_299 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline_300 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
)
@@ -31,7 +31,7 @@
(i32.const 0)
(global.get $global0)
(ref.func $"foo tear-off dynamic call entry")
(ref.func $"foo tear-off trampoline_307")
(ref.func $"foo tear-off trampoline_308")
(struct.new $#Vtable-0-2)
(i32.const 12)
(i32.const 0)
@@ -57,7 +57,7 @@
(i32.const 0)
(global.get $global0)
(ref.func $"bar tear-off dynamic call entry")
(ref.func $"bar tear-off trampoline_313")
(ref.func $"bar tear-off trampoline_314")
(struct.new $#Vtable-0-2)
(i32.const 12)
(i32.const 0)
@@ -74,7 +74,7 @@
(struct.new $#Closure-0-2))
(global $global0 (ref $#DummyStruct) <...>)
(func $bar tear-off dynamic call entry (param $var0 (ref $#Closure-0-0)) (param $var1 (ref $Array<_Type>)) (param $var2 (ref $Array<Object?>)) (param $var3 (ref $Array<Object?>)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_313 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_314 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off dynamic call entry (param $var0 (ref $#Closure-0-0)) (param $var1 (ref $Array<_Type>)) (param $var2 (ref $Array<Object?>)) (param $var3 (ref $Array<Object?>)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline_307 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $foo tear-off trampoline_308 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
)
@@ -887,7 +887,7 @@ Map<String, dynamic>? diffToJson(ProgramInfo diff,
keepOnlyInputPackage ? key != 'package:input' : key.startsWith('file:'));
// Rebuild the diff JSON discarding all nodes with size below threshold.
const smallChangeThreshold = 16;
const smallChangeThreshold = 13;
Map<String, dynamic>? discardSmallChanges(Map<String, dynamic> map) {
final result = <String, dynamic>{};
@@ -121,3 +121,7 @@ abstract interface class IsolateGroup {
static Object _runSync(Object computation) =>
throw UnsupportedError("_runSync");
}
final List<Object> _toStringVisiting = [];
@patch
List<Object> get toStringVisiting => _toStringVisiting;
@@ -99,3 +99,7 @@ abstract interface class IsolateGroup {
static Object _runSync(Object computation) =>
throw UnsupportedError("_runSync");
}
final List<Object> _toStringVisiting = [];
@patch
List<Object> get toStringVisiting => _toStringVisiting;
@@ -12,6 +12,7 @@ import "dart:core" hide Symbol;
import "dart:ffi" show Pointer, Struct, Union, IntPtr, Handle, Void, Native;
import "dart:isolate" show SendPort;
import "dart:typed_data" show Int32List, Uint8List;
import "dart:_vm" show FinalThreadLocal;
/// These are the additional parts of this patch library:
part "class_id_fasta.dart";
@@ -482,3 +483,8 @@ abstract interface class IsolateGroup {
@Native<Handle Function(Handle)>(symbol: "IsolateGroup_runSync")
external static Object _runSync(Object computation);
}
@pragma("vm:shared")
final _toStringVisiting = FinalThreadLocal<List<Object>>(() => <Object>[]);
@patch
List<Object> get toStringVisiting => _toStringVisiting.value;
@@ -239,3 +239,7 @@ abstract interface class IsolateGroup {
static Object _runSync(Object computation) =>
throw UnsupportedError("_runSync");
}
final List<Object> _toStringVisiting = [];
@patch
List<Object> get toStringVisiting => _toStringVisiting;
+1
View File
@@ -58,6 +58,7 @@ import 'dart:_internal' hide Symbol;
import 'dart:math' show Random; // Used by ListMixin.shuffle.
export 'dart:_internal' show DoubleLinkedQueueEntry;
import 'dart:_internal' show isToStringVisiting, toStringVisiting;
part 'collections.dart';
part 'hash_map.dart';
+1
View File
@@ -153,6 +153,7 @@ library dart.core;
import "dart:collection";
import "dart:_internal" hide Symbol, LinkedList, LinkedListEntry;
import "dart:_internal" as internal show Symbol;
import "dart:_internal" show isToStringVisiting, toStringVisiting;
import "dart:convert"
show
ascii,
+4 -3
View File
@@ -1124,12 +1124,13 @@ const vmIsolateUnsendable = pragma("vm:isolate-unsendable");
// Helpers used to detect cycles in collection `toString`s.
/// A collection used to identify cyclic lists during `toString` calls.
final List<Object> toStringVisiting = [];
external List<Object> get toStringVisiting;
/// Check if we are currently visiting [object] in a `toString` call.
bool isToStringVisiting(Object object) {
for (int i = 0; i < toStringVisiting.length; i++) {
if (identical(object, toStringVisiting[i])) return true;
final visiting = toStringVisiting;
for (int i = 0; i < visiting.length; i++) {
if (identical(object, visiting[i])) return true;
}
return false;
}
+14
View File
@@ -60,6 +60,7 @@ main(List<String> args) {
testBase64Decoder();
testRandom();
testEncoding();
testRecursiveToString();
print("All tests completed :)");
}
@@ -438,3 +439,16 @@ void testEncoding() {
IsolateGroup.runSync(() => Encoding.getByName("utf-8")),
);
}
///
void testRecursiveToString() {
Expect.equals(
"[foo, bar, [...], baz]",
IsolateGroup.runSync(() {
var l = <Object>["foo", "bar"];
l.add(l);
l.add("baz");
return l.toString();
}),
);
}