[vm] Avoid expensive runtime function type checks in Future implementation The `_FutureListener.callback` field is used for multiple purposes (possibly to save memory by avoiding additional fields) so it cannot have a specific function type. This causes the `Function?` typed `_FutureListener.callback` field to be casted to proper function types such as `T Function(S)`. => Those runtime type checks are very expensive. To avoid introducing more fields we use an `unsafeCast<T>()` for those casts, since the implementation guarantees they are going to suceed. Issue https://github.com/dart-lang/sdk/issues/48225 TEST=ci Change-Id: I6f0cc87600462c8ca5baceeb511ce8a06c61237e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/230240 Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Lasse Nielsen <lrn@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/front_end/testcases/general/external_import.dart.weak.expect b/pkg/front_end/testcases/general/external_import.dart.weak.expect index 0d7662f..5e7e2df 100644 --- a/pkg/front_end/testcases/general/external_import.dart.weak.expect +++ b/pkg/front_end/testcases/general/external_import.dart.weak.expect
@@ -37,5 +37,5 @@ Constructor coverage from constants: org-dartlang-testcase:///external_import.dart: -- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:92:9) +- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:109:9) - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/external_import.dart.weak.modular.expect b/pkg/front_end/testcases/general/external_import.dart.weak.modular.expect index 0d7662f..5e7e2df 100644 --- a/pkg/front_end/testcases/general/external_import.dart.weak.modular.expect +++ b/pkg/front_end/testcases/general/external_import.dart.weak.modular.expect
@@ -37,5 +37,5 @@ Constructor coverage from constants: org-dartlang-testcase:///external_import.dart: -- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:92:9) +- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:109:9) - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/external_import.dart.weak.transformed.expect b/pkg/front_end/testcases/general/external_import.dart.weak.transformed.expect index 0d7662f..5e7e2df 100644 --- a/pkg/front_end/testcases/general/external_import.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general/external_import.dart.weak.transformed.expect
@@ -37,5 +37,5 @@ Constructor coverage from constants: org-dartlang-testcase:///external_import.dart: -- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:92:9) +- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:109:9) - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.expect b/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.expect index 7670aed..b24cb95 100644 --- a/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.expect +++ b/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.expect
@@ -42,5 +42,5 @@ Constructor coverage from constants: org-dartlang-testcase:///native_is_illegal.dart: -- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:92:9) +- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:109:9) - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.modular.expect b/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.modular.expect index 7670aed..b24cb95 100644 --- a/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.modular.expect +++ b/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.modular.expect
@@ -42,5 +42,5 @@ Constructor coverage from constants: org-dartlang-testcase:///native_is_illegal.dart: -- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:92:9) +- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:109:9) - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.transformed.expect b/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.transformed.expect index 7670aed..b24cb95 100644 --- a/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/rasta/native_is_illegal.dart.weak.transformed.expect
@@ -42,5 +42,5 @@ Constructor coverage from constants: org-dartlang-testcase:///native_is_illegal.dart: -- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:92:9) +- ExternalName. (from org-dartlang-sdk:///sdk/lib/internal/internal.dart:109:9) - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/internal_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/internal_patch.dart index f4ab556..ca3bd4f 100644 --- a/sdk/lib/_internal/js_dev_runtime/patch/internal_patch.dart +++ b/sdk/lib/_internal/js_dev_runtime/patch/internal_patch.dart
@@ -62,3 +62,6 @@ @patch bool isSentinel(dynamic value) => throw UnsupportedError('isSentinel'); + +@patch +T unsafeCast<T>(dynamic v) => v;
diff --git a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart index 9bf6da6..ef27959 100644 --- a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
@@ -73,3 +73,7 @@ @patch @pragma('dart2js:tryInline') bool isSentinel(dynamic value) => isJsSentinel(value); + +@patch +@pragma('dart2js:tryInline') +T unsafeCast<T>(dynamic v) => v;
diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart index b8141d6..3d7a3cd8f 100644 --- a/sdk/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk/lib/_internal/vm/lib/internal_patch.dart
@@ -156,13 +156,9 @@ return newStack; } -// This function can be used to skip implicit or explicit checked down casts in -// the parts of the core library implementation where we know by construction the -// type of a value. -// -// Important: this is unsafe and must be used with care. +@patch @pragma("vm:external-name", "Internal_unsafeCast") -external T unsafeCast<T>(Object? v); +external T unsafeCast<T>(dynamic v); // This function can be used to keep an object alive till that point. @pragma("vm:recognized", "other")
diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart index 0b16dae..ca05fc6 100644 --- a/sdk/lib/async/async.dart +++ b/sdk/lib/async/async.dart
@@ -114,7 +114,8 @@ printToZone, printToConsole, Since, - typeAcceptsNull; + typeAcceptsNull, + unsafeCast; part 'async_error.dart'; part 'broadcast_stream_controller.dart';
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart index 4d13cc8..26cfecd 100644 --- a/sdk/lib/async/future_impl.dart +++ b/sdk/lib/async/future_impl.dart
@@ -4,15 +4,6 @@ part of dart.async; -/// The onValue and onError handlers return either a value or a future -typedef FutureOr<T> _FutureOnValue<S, T>(S value); - -/// Test used by [Future.catchError] to handle skip some errors. -typedef bool _FutureErrorTest(Object error); - -/// Used by [WhenFuture]. -typedef dynamic _FutureAction(); - abstract class _Completer<T> implements Completer<T> { final _Future<T> future = new _Future<T>(); @@ -106,7 +97,7 @@ state = (errorCallback == null) ? stateThen : stateThenOnerror; _FutureListener.thenAwait( - this.result, _FutureOnValue<S, T> onValue, Function errorCallback) + this.result, FutureOr<T> Function(S) onValue, Function errorCallback) : callback = onValue, errorCallback = errorCallback, state = stateThenOnerror; @@ -127,19 +118,19 @@ FutureOr<T> Function(S) get _onValue { assert(handlesValue); - return callback as dynamic; + return unsafeCast<FutureOr<T> Function(S)>(callback); } Function? get _onError => errorCallback; - _FutureErrorTest get _errorTest { + bool Function(Object) get _errorTest { assert(hasErrorTest); - return callback as dynamic; + return unsafeCast<bool Function(Object)>(callback); } - _FutureAction get _whenCompleteAction { + dynamic Function() get _whenCompleteAction { assert(handlesComplete); - return callback as dynamic; + return unsafeCast<dynamic Function()>(callback); } /// Whether this listener has an error callback.
diff --git a/sdk/lib/internal/internal.dart b/sdk/lib/internal/internal.dart index 5d38bd8..a7a7eda 100644 --- a/sdk/lib/internal/internal.dart +++ b/sdk/lib/internal/internal.dart
@@ -35,6 +35,23 @@ // execution mode. external bool typeAcceptsNull<T>(); +/// Unsafely treats [value] as type [T]. +/// +/// An unsafe cast allows casting any value to any type, +/// without any runtime type checks. +/// +/// Can be used internally in platform library implementations of +/// data structures, where a value is known to have a type different +/// from its static type (like knowing that a string is definitely +/// a "_OneByteString" or that the value stored into a heterogenous +/// list is really a value of the surrounding map). +/// +/// Must only be used for casts which would definitely *succeed* +/// as a normal cast. +/// +/// Should only be used for performance in performance critical code. +external T unsafeCast<T>(dynamic value); + // Powers of 10 up to 10^22 are representable as doubles. // Powers of 10 above that are only approximate due to lack of precission. // Used by double-parsing.