[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>
This commit is contained in:
Martin Kustermann
2022-02-04 14:19:22 +00:00
committed by Commit Bot
parent 860f7c532d
commit ddcd028cde
12 changed files with 40 additions and 28 deletions
@@ -37,5 +37,5 @@ constants {
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)
@@ -37,5 +37,5 @@ constants {
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)
@@ -37,5 +37,5 @@ constants {
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)
@@ -42,5 +42,5 @@ constants {
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)
@@ -42,5 +42,5 @@ constants {
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)
@@ -42,5 +42,5 @@ constants {
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)
@@ -62,3 +62,6 @@ T createSentinel<T>() => throw UnsupportedError('createSentinel');
@patch
bool isSentinel(dynamic value) => throw UnsupportedError('isSentinel');
@patch
T unsafeCast<T>(dynamic v) => v;
@@ -73,3 +73,7 @@ T createSentinel<T>() => createJsSentinel<T>();
@patch
@pragma('dart2js:tryInline')
bool isSentinel(dynamic value) => isJsSentinel(value);
@patch
@pragma('dart2js:tryInline')
T unsafeCast<T>(dynamic v) => v;
+2 -6
View File
@@ -156,13 +156,9 @@ Int32List _growRegExpStack(Int32List stack) {
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")
+2 -1
View File
@@ -114,7 +114,8 @@ import "dart:_internal"
printToZone,
printToConsole,
Since,
typeAcceptsNull;
typeAcceptsNull,
unsafeCast;
part 'async_error.dart';
part 'broadcast_stream_controller.dart';
+6 -15
View File
@@ -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 @@ class _FutureListener<S, T> {
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 @@ class _FutureListener<S, T> {
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.
+17
View File
@@ -35,6 +35,23 @@ part 'symbol.dart';
// 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.