From b8d25b791a01dff1fac6dfd7dd94ceab602e8cdb Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 31 Mar 2025 04:10:39 -0700 Subject: [PATCH] [dart2wasm] Make slow path call `StackTrace.current` on null errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For throwing normal exceptions we already outlined obtaining the current stack. We can do this for null check errors as well. Change-Id: Ie0505bfd99ff9c6738040c47b07e542439ee74a5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419161 Reviewed-by: Ömer Ağacan Commit-Queue: Martin Kustermann --- pkg/dart2wasm/lib/async.dart | 14 ++++++++++---- pkg/dart2wasm/lib/code_generator.dart | 3 +-- pkg/dart2wasm/lib/kernel_nodes.dart | 7 +++++-- sdk/lib/_internal/wasm/lib/async_patch.dart | 9 +++++++++ sdk/lib/_internal/wasm/lib/errors_patch.dart | 4 +++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart index 9e376be11c2..991dd3b07a5 100644 --- a/pkg/dart2wasm/lib/async.dart +++ b/pkg/dart2wasm/lib/async.dart @@ -274,6 +274,15 @@ class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator { b.return_(); } + void callCompleteErrorWithCurrentStack() { + b.local_get(_suspendStateLocal); + b.struct_get( + asyncSuspendStateInfo.struct, FieldIndex.asyncSuspendStateCompleter); + b.local_get(exceptionLocal); + call(translator.completerCompleteErrorWithCurrentStack.reference); + b.return_(); + } + // Handle Dart exceptions. b.catch_legacy(translator.getExceptionTag(b.module)); b.local_set(stackTraceLocal); @@ -289,10 +298,7 @@ class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator { // JS exceptions won't have a Dart stack trace, so we attach the current // Dart stack trace. - call(translator.stackTraceCurrent.reference); - b.local_set(stackTraceLocal); - - callCompleteError(); + callCompleteErrorWithCurrentStack(); b.end(); // try diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart index e7c75856bf0..75c079cf5c9 100644 --- a/pkg/dart2wasm/lib/code_generator.dart +++ b/pkg/dart2wasm/lib/code_generator.dart @@ -2651,8 +2651,7 @@ abstract class AstCodeGenerator // We lower a null check to a br_on_non_null, throwing a [TypeError] in // the null case. b.br_on_non_null(nullCheckBlock); - call(translator.stackTraceCurrent.reference); - call(translator.throwNullCheckError.reference); + call(translator.throwNullCheckErrorWithCurrentStack.reference); b.unreachable(); b.end(); return nonNullOperandType; diff --git a/pkg/dart2wasm/lib/kernel_nodes.dart b/pkg/dart2wasm/lib/kernel_nodes.dart index e87191812bf..8bd6019e3fa 100644 --- a/pkg/dart2wasm/lib/kernel_nodes.dart +++ b/pkg/dart2wasm/lib/kernel_nodes.dart @@ -147,6 +147,9 @@ mixin KernelNodes { index.getField("dart:async", "_Completer", "future"); late final Procedure completerComplete = index.getProcedure("dart:async", "_AsyncCompleter", "complete"); + late final Procedure completerCompleteErrorWithCurrentStack = + index.getProcedure( + "dart:async", "_AsyncCompleter", "_completeErrorWithCurrentStack"); late final Procedure completerCompleteError = index.getProcedure("dart:async", "_Completer", "completeError"); late final Procedure awaitHelper = @@ -290,8 +293,8 @@ mixin KernelNodes { "_throwUnimplementedExternalMemberError"); late final Procedure stackTraceCurrent = index.getProcedure("dart:core", "StackTrace", "get:current"); - late final Procedure throwNullCheckError = - index.getProcedure("dart:core", "_TypeError", "_throwNullCheckError"); + late final Procedure throwNullCheckErrorWithCurrentStack = index.getProcedure( + "dart:core", "_TypeError", "_throwNullCheckErrorWithCurrentStack"); late final Procedure throwAsCheckError = index.getProcedure("dart:core", "_TypeError", "_throwAsCheckError"); late final Procedure throwInterfaceTypeAsCheckError1 = index diff --git a/sdk/lib/_internal/wasm/lib/async_patch.dart b/sdk/lib/_internal/wasm/lib/async_patch.dart index ef313408e45..118da9bbd8e 100644 --- a/sdk/lib/_internal/wasm/lib/async_patch.dart +++ b/sdk/lib/_internal/wasm/lib/async_patch.dart @@ -84,6 +84,15 @@ _AsyncSuspendState _newAsyncSuspendState( @pragma("wasm:entry-point") _AsyncCompleter _makeAsyncCompleter() => _AsyncCompleter(); +@patch +@pragma("wasm:entry-point") +class _AsyncCompleter extends _Completer { + @pragma("wasm:entry-point") + void _completeErrorWithCurrentStack(Object error) { + completeError(error, StackTrace.current); + } +} + @pragma("wasm:entry-point") void _awaitHelper(_AsyncSuspendState suspendState, Object? operand) { if (operand is! Future) { diff --git a/sdk/lib/_internal/wasm/lib/errors_patch.dart b/sdk/lib/_internal/wasm/lib/errors_patch.dart index 24c34511c74..d6a7128d67b 100644 --- a/sdk/lib/_internal/wasm/lib/errors_patch.dart +++ b/sdk/lib/_internal/wasm/lib/errors_patch.dart @@ -73,7 +73,9 @@ class _TypeError extends _Error implements TypeError { } @pragma("wasm:entry-point") - static Never _throwNullCheckError(StackTrace stackTrace) { + @pragma("wasm:never-inline") + static Never _throwNullCheckErrorWithCurrentStack() { + final stackTrace = StackTrace.current; final typeError = _TypeError.fromMessageAndStackTrace( "Null check operator used on a null value", stackTrace,