From e688981385bd589a915d6af3cf1534a3a3a7a3e4 Mon Sep 17 00:00:00 2001 From: Nate Biggs Date: Wed, 21 May 2025 17:49:22 -0700 Subject: [PATCH] [ddc] Fix capture issue with temp names within async scopes. `needsCapture` will ensure that any variables used within an async scope get included in the 'asyncScope' object that's created for that scope. The variables used to lower Dart late variables in particular get emitted separately. But they can still be used across async scopes so they need the special capture logic as well. Bug: https://github.com/dart-lang/sdk/issues/60748 Change-Id: I2486fce41f88f186fd799029c2cc59635f7ad8f5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/429780 Reviewed-by: Nicholas Shahan Commit-Queue: Nate Biggs --- pkg/dev_compiler/lib/src/kernel/compiler.dart | 3 +- .../lib/src/kernel/compiler_new.dart | 3 +- tests/web/regress/issue/60748_test.dart | 33 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/web/regress/issue/60748_test.dart diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index 157f42277eb..d1ba90a179c 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart @@ -4918,7 +4918,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor if (_isTemporaryVariable(v)) { var name = _debuggerFriendlyTemporaryVariableName(v); name ??= 't\$${_tempVariables.length}'; - return _tempVariables.putIfAbsent(v, () => _emitScopedId(name!)); + return _tempVariables.putIfAbsent( + v, () => _emitScopedId(name!, needsCapture: true)); } var name = v.name!; if (isLateLoweredLocal(v)) { diff --git a/pkg/dev_compiler/lib/src/kernel/compiler_new.dart b/pkg/dev_compiler/lib/src/kernel/compiler_new.dart index dc0bda3e36f..12dc2e2b7aa 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler_new.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler_new.dart @@ -5403,7 +5403,8 @@ class LibraryCompiler extends ComputeOnceConstantVisitor if (_isTemporaryVariable(v)) { var name = _debuggerFriendlyTemporaryVariableName(v); name ??= 't\$${_tempVariables.length}'; - return _tempVariables.putIfAbsent(v, () => _emitScopedId(name!)); + return _tempVariables.putIfAbsent( + v, () => _emitScopedId(name!, needsCapture: true)); } var name = v.name!; if (isLateLoweredLocal(v)) { diff --git a/tests/web/regress/issue/60748_test.dart b/tests/web/regress/issue/60748_test.dart new file mode 100644 index 00000000000..6874f9d10a7 --- /dev/null +++ b/tests/web/regress/issue/60748_test.dart @@ -0,0 +1,33 @@ +// 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. + +import 'package:expect/async_helper.dart'; +import 'package:expect/expect.dart'; + +Future main() async { + asyncStart(); + List callbacks = []; + List expectedHashCodes = []; + + void save(Object o) { + expectedHashCodes.add(o.hashCode); + } + + void check(Object o, int i) { + Expect.equals(expectedHashCodes[i], o.hashCode); + } + + for (int i = 0; i < 3; i++) { + late Object o = Object(); + o; + Object record() => o; + callbacks.add(record); + save(record()); + } + + for (int i = callbacks.length - 1; i >= 0; i--) { + check(callbacks[i](), i); + } + asyncEnd(); +}