[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.