From e44bc22ca3a6d3081d35ce1f267e3fc217d18abb Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 24 Apr 2024 10:03:00 +0000 Subject: [PATCH] [dart2wasm] Fix bug in restoration of `this` in async functions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When entering async functions we read the suspend-state's context and populate local variables with the context parent chain as well as the this pointer. This restoration code assumed that `this` is stored in the outermost context, which isn't necessarily the case. In constructors the outermost context can contain the type parameters. TEST=tests/web/wasm/capture_type_and_this_test.dart Change-Id: Ie8e3c8732203aea4964d48cb78c97578d0322b2b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364321 Reviewed-by: Ömer Ağacan Commit-Queue: Martin Kustermann --- pkg/dart2wasm/lib/async.dart | 33 +++++++++++-------- .../web/wasm/capture_type_and_this_test.dart | 32 ++++++++++++++++++ 2 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 tests/web/wasm/capture_type_and_this_test.dart diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart index 6f387eaee8d..d39a4550ade 100644 --- a/pkg/dart2wasm/lib/async.dart +++ b/pkg/dart2wasm/lib/async.dart @@ -898,19 +898,26 @@ class AsyncCodeGenerator extends CodeGenerator { _cloneContext(cloneContextFor!, context, context.currentLocal); } - while (context!.parent != null) { - assert(!context.parent!.isEmpty); - b.local_get(context.currentLocal); - b.struct_get(context.struct, context.parentFieldIndex); - b.ref_as_non_null(); - context = context.parent!; - b.local_set(context.currentLocal); - } - if (context.containsThis) { - b.local_get(context.currentLocal); - b.struct_get(context.struct, context.thisFieldIndex); - b.ref_as_non_null(); - b.local_set(thisLocal!); + bool restoredThis = false; + while (context != null) { + if (context.containsThis) { + assert(!restoredThis); + b.local_get(context.currentLocal); + b.struct_get(context.struct, context.thisFieldIndex); + b.ref_as_non_null(); + b.local_set(thisLocal!); + restoredThis = true; + } + + final parent = context.parent; + if (parent != null) { + assert(!parent.isEmpty); + b.local_get(context.currentLocal); + b.struct_get(context.struct, context.parentFieldIndex); + b.ref_as_non_null(); + b.local_set(parent.currentLocal); + } + context = parent; } } } diff --git a/tests/web/wasm/capture_type_and_this_test.dart b/tests/web/wasm/capture_type_and_this_test.dart new file mode 100644 index 00000000000..b487c577c59 --- /dev/null +++ b/tests/web/wasm/capture_type_and_this_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2024, 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. + +late final Type capturedType; +late final A capturedThis; + +class A { + A() { + foo() async { + // This will create a context chain as follows: + // Context [T] + // `--> Context [, this] + // `--> Context [(); + + if (!identical(a, capturedThis)) { + throw 'Should have captured the correct `this`.'; + } + if (!identical(String, capturedType)) { + throw 'Should have captured the correct `T`.'; + } +}