mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:39:19 +00:00
[dart:js_interop] Make ExternalDartReference generic
Closes https://github.com/dart-lang/sdk/issues/55342 Closes https://github.com/dart-lang/sdk/issues/55536 Closes https://github.com/dart-lang/sdk/issues/56015 - Adds a type parameter T that extends Object? to ExternalDartReference to capture the type of the value that was externalized. -- In the JS compilers, the representation type of ExternalDartReference is now T. -- In dart2wasm, the representation type is now JSValue?. - ExternalDartReference no longer implements Object. - Return type of toDartObject is now T. - ObjectToExternalDartReference and ExternalDartReferenceToObject both now are on a T that is bound to Object?. - Internal patches for WeakReference and Finalizer are updated. Change-Id: Ic2dc834b17ec6a4eb2122cba3c495a6e0a1eae6e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/370663 Commit-Queue: Srujan Gaddam <srujzs@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com> Reviewed-by: Lasse Nielsen <lrn@google.com>
This commit is contained in:
parent
2653075ac8
commit
b8402f5c27
|
@ -57,9 +57,18 @@
|
|||
of a `String` to support other JS values as well, like `TrustedScriptURL`s.
|
||||
- **Breaking Change** [#55267][]: `isTruthy` and `not` now return `JSBoolean`
|
||||
instead of `bool` to be consistent with the other operators.
|
||||
- **Breaking Change** `ExternalDartReference` no longer implements `Object`.
|
||||
`ExternalDartReference` now accepts a type parameter `T` with a bound of
|
||||
`Object?` to capture the type of the Dart object that is externalized.
|
||||
`ExternalDartReferenceToObject.toDartObject` now returns a `T`.
|
||||
`ExternalDartReferenceToObject` and `ObjectToExternalDartReference` are now
|
||||
extensions on `T` and `ExternalDartReference<T>`, respectively, where `T
|
||||
extends Object?`. See [#55342][] and [#55536][] for more details.
|
||||
|
||||
[#55508]: https://github.com/dart-lang/sdk/issues/55508
|
||||
[#55267]: https://github.com/dart-lang/sdk/issues/55267
|
||||
[#55342]: https://github.com/dart-lang/sdk/issues/55342
|
||||
[#55536]: https://github.com/dart-lang/sdk/issues/55536
|
||||
|
||||
### Tools
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class CallbackSpecializer {
|
|||
VariableGet v = VariableGet(positionalParameters[i]);
|
||||
if (_util.isJSValueType(callbackParameterType) && boxExternRef) {
|
||||
expression = _createJSValue(v);
|
||||
if (!callbackParameterType.isPotentiallyNullable) {
|
||||
if (!callbackParameterType.extensionTypeErasure.isPotentiallyNullable) {
|
||||
expression = NullCheck(expression);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -70,7 +70,7 @@ class CoreTypesUtil {
|
|||
Procedure jsifyTarget(DartType type) =>
|
||||
isJSValueType(type) ? jsValueUnboxTarget : jsifyRawTarget;
|
||||
|
||||
/// Return whether [type] erases to a `JSValue`.
|
||||
/// Whether [type] erases to a `JSValue` or `JSValue?`.
|
||||
bool isJSValueType(DartType type) =>
|
||||
_extensionIndex.isStaticInteropType(type) ||
|
||||
_extensionIndex.isExternalDartReferenceType(type);
|
||||
|
@ -112,7 +112,7 @@ class CoreTypesUtil {
|
|||
// there are static interop types that are not boxed as JSValue, we
|
||||
// might need a proper cast then.
|
||||
expression = invokeOneArg(jsValueBoxTarget, invocation);
|
||||
if (returnType.isPotentiallyNonNullable) {
|
||||
if (returnType.extensionTypeErasure.isPotentiallyNonNullable) {
|
||||
expression = NullCheck(expression);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -19,7 +19,7 @@ JSObjectRepType _createObjectLiteral() =>
|
|||
@pragma('dart2js:prefer-inline')
|
||||
JSObject get globalContext => staticInteropGlobalContext as JSObject;
|
||||
|
||||
/// Helper for working with the [JSAny?] top type in a backend agnostic way.
|
||||
// Helper for working with the JSAny? top type in a backend agnostic way.
|
||||
@patch
|
||||
extension NullableUndefineableJSAnyExtension on JSAny? {
|
||||
@patch
|
||||
|
@ -53,7 +53,7 @@ extension JSAnyUtilityExtension on JSAny? {
|
|||
Object? dartify() => js_util.dartify(this);
|
||||
}
|
||||
|
||||
/// Utility extensions for [Object?].
|
||||
// Utility extensions for Object?.
|
||||
@patch
|
||||
extension NullableObjectUtilExtension on Object? {
|
||||
@patch
|
||||
|
@ -61,7 +61,8 @@ extension NullableObjectUtilExtension on Object? {
|
|||
JSAny? jsify() => js_util.jsify(this);
|
||||
}
|
||||
|
||||
/// [JSExportedDartFunction] <-> [Function]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSExportedDartFunction <-> Function
|
||||
@patch
|
||||
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
|
||||
// TODO(srujzs): We should unwrap rather than allow arbitrary JS functions
|
||||
|
@ -79,15 +80,16 @@ extension FunctionToJSExportedDartFunction on Function {
|
|||
js_util.allowInterop(this) as JSExportedDartFunction;
|
||||
}
|
||||
|
||||
/// Embedded global property for wrapped Dart objects passed via JS interop.
|
||||
///
|
||||
/// This is a Symbol so that different Dart applications don't share Dart
|
||||
/// objects from different Dart runtimes. We expect all [JSBoxedDartObject]s to
|
||||
/// have this Symbol.
|
||||
// Embedded global property for wrapped Dart objects passed via JS interop.
|
||||
//
|
||||
// This is a Symbol so that different Dart applications don't share Dart
|
||||
// objects from different Dart runtimes. We expect all JSBoxedDartObjects to
|
||||
// have this Symbol.
|
||||
final Object _jsBoxedDartObjectProperty =
|
||||
foreign_helper.JS('', 'Symbol("jsBoxedDartObjectProperty")');
|
||||
|
||||
/// [JSBoxedDartObject] <-> [Object]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSBoxedDartObject <-> Object
|
||||
@patch
|
||||
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
|
||||
@patch
|
||||
|
@ -118,23 +120,25 @@ extension ObjectToJSBoxedDartObject on Object {
|
|||
}
|
||||
}
|
||||
|
||||
/// [ExternalDartReference] <-> [Object]
|
||||
// -----------------------------------------------------------------------------
|
||||
// ExternalDartReference <-> T
|
||||
@patch
|
||||
extension ExternalDartReferenceToObject on ExternalDartReference {
|
||||
extension ExternalDartReferenceToObject<T extends Object?>
|
||||
on ExternalDartReference<T> {
|
||||
@patch
|
||||
@pragma('dart2js:prefer-inline')
|
||||
Object get toDartObject => this;
|
||||
T get toDartObject => _externalDartReference;
|
||||
}
|
||||
|
||||
@patch
|
||||
extension ObjectToExternalDartReference on Object {
|
||||
extension ObjectToExternalDartReference<T extends Object?> on T {
|
||||
@patch
|
||||
@pragma('dart2js:prefer-inline')
|
||||
ExternalDartReference get toExternalReference =>
|
||||
ExternalDartReference._(this);
|
||||
ExternalDartReference<T> get toExternalReference =>
|
||||
ExternalDartReference<T>._(this);
|
||||
}
|
||||
|
||||
/// [JSPromise] -> [Future].
|
||||
// JSPromise -> Future
|
||||
@patch
|
||||
extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
||||
@patch
|
||||
|
@ -142,7 +146,8 @@ extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
|||
Future<T> get toDart => js_util.promiseToFuture<T>(this);
|
||||
}
|
||||
|
||||
/// [JSArrayBuffer] <-> [ByteBuffer]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSArrayBuffer <-> ByteBuffer
|
||||
@patch
|
||||
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
|
||||
@patch
|
||||
|
@ -157,7 +162,8 @@ extension ByteBufferToJSArrayBuffer on ByteBuffer {
|
|||
JSArrayBuffer get toJS => this as JSArrayBuffer;
|
||||
}
|
||||
|
||||
/// [JSDataView] <-> [ByteData]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSDataView <-> ByteData
|
||||
@patch
|
||||
extension JSDataViewToByteData on JSDataView {
|
||||
@patch
|
||||
|
@ -172,7 +178,8 @@ extension ByteDataToJSDataView on ByteData {
|
|||
JSDataView get toJS => this as JSDataView;
|
||||
}
|
||||
|
||||
/// [JSInt8Array] <-> [Int8List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSInt8Array <-> Int8List
|
||||
@patch
|
||||
extension JSInt8ArrayToInt8List on JSInt8Array {
|
||||
@patch
|
||||
|
@ -187,7 +194,8 @@ extension Int8ListToJSInt8Array on Int8List {
|
|||
JSInt8Array get toJS => this as JSInt8Array;
|
||||
}
|
||||
|
||||
/// [JSUint8Array] <-> [Uint8List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint8Array <-> Uint8List
|
||||
@patch
|
||||
extension JSUint8ArrayToUint8List on JSUint8Array {
|
||||
@patch
|
||||
|
@ -202,7 +210,8 @@ extension Uint8ListToJSUint8Array on Uint8List {
|
|||
JSUint8Array get toJS => this as JSUint8Array;
|
||||
}
|
||||
|
||||
/// [JSUint8ClampedArray] <-> [Uint8ClampedList]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint8ClampedArray <-> Uint8ClampedList
|
||||
@patch
|
||||
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
|
||||
@patch
|
||||
|
@ -217,7 +226,8 @@ extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList {
|
|||
JSUint8ClampedArray get toJS => this as JSUint8ClampedArray;
|
||||
}
|
||||
|
||||
/// [JSInt16Array] <-> [Int16List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSInt16Array <-> Int16List
|
||||
@patch
|
||||
extension JSInt16ArrayToInt16List on JSInt16Array {
|
||||
@patch
|
||||
|
@ -232,7 +242,8 @@ extension Int16ListToJSInt16Array on Int16List {
|
|||
JSInt16Array get toJS => this as JSInt16Array;
|
||||
}
|
||||
|
||||
/// [JSUint16Array] <-> [Uint16List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint16Array <-> Uint16List
|
||||
@patch
|
||||
extension JSUint16ArrayToInt16List on JSUint16Array {
|
||||
@patch
|
||||
|
@ -247,7 +258,8 @@ extension Uint16ListToJSInt16Array on Uint16List {
|
|||
JSUint16Array get toJS => this as JSUint16Array;
|
||||
}
|
||||
|
||||
/// [JSInt32Array] <-> [Int32List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSInt32Array <-> Int32List
|
||||
@patch
|
||||
extension JSInt32ArrayToInt32List on JSInt32Array {
|
||||
@patch
|
||||
|
@ -262,7 +274,8 @@ extension Int32ListToJSInt32Array on Int32List {
|
|||
JSInt32Array get toJS => this as JSInt32Array;
|
||||
}
|
||||
|
||||
/// [JSUint32Array] <-> [Uint32List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint32Array <-> Uint32List
|
||||
@patch
|
||||
extension JSUint32ArrayToUint32List on JSUint32Array {
|
||||
@patch
|
||||
|
@ -277,7 +290,8 @@ extension Uint32ListToJSUint32Array on Uint32List {
|
|||
JSUint32Array get toJS => this as JSUint32Array;
|
||||
}
|
||||
|
||||
/// [JSFloat32Array] <-> [Float32List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSFloat32Array <-> Float32List
|
||||
@patch
|
||||
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
|
||||
@patch
|
||||
|
@ -292,7 +306,8 @@ extension Float32ListToJSFloat32Array on Float32List {
|
|||
JSFloat32Array get toJS => this as JSFloat32Array;
|
||||
}
|
||||
|
||||
/// [JSFloat64Array] <-> [Float64List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSFloat64Array <-> Float64List
|
||||
@patch
|
||||
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
|
||||
@patch
|
||||
|
@ -307,7 +322,8 @@ extension Float64ListToJSFloat64Array on Float64List {
|
|||
JSFloat64Array get toJS => this as JSFloat64Array;
|
||||
}
|
||||
|
||||
/// [JSArray] <-> [List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSArray <-> List
|
||||
@patch
|
||||
extension JSArrayToList<T extends JSAny?> on JSArray<T> {
|
||||
@patch
|
||||
|
@ -339,7 +355,8 @@ extension ListToJSArray<T extends JSAny?> on List<T> {
|
|||
JSArray<T> get toJSProxyOrRef => this as JSArray<T>;
|
||||
}
|
||||
|
||||
/// [JSNumber] -> [double] or [int].
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSNumber -> double or int
|
||||
@patch
|
||||
extension JSNumberToNumber on JSNumber {
|
||||
@patch
|
||||
|
@ -351,7 +368,8 @@ extension JSNumberToNumber on JSNumber {
|
|||
int get toDartInt => this as int;
|
||||
}
|
||||
|
||||
/// [double] -> [JSNumber].
|
||||
// -----------------------------------------------------------------------------
|
||||
// double -> JSNumber
|
||||
@patch
|
||||
extension DoubleToJSNumber on double {
|
||||
@patch
|
||||
|
@ -359,7 +377,8 @@ extension DoubleToJSNumber on double {
|
|||
JSNumber get toJS => JSNumber._(this);
|
||||
}
|
||||
|
||||
/// [JSBoolean] <-> [bool]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSBoolean <-> bool
|
||||
@patch
|
||||
extension JSBooleanToBool on JSBoolean {
|
||||
@patch
|
||||
|
@ -374,7 +393,8 @@ extension BoolToJSBoolean on bool {
|
|||
JSBoolean get toJS => JSBoolean._(this);
|
||||
}
|
||||
|
||||
/// [JSString] <-> [String]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSString <-> String
|
||||
@patch
|
||||
extension JSStringToString on JSString {
|
||||
@patch
|
||||
|
|
|
@ -66,7 +66,7 @@ typedef JSBigIntRepType = interceptors.JavaScriptBigInt;
|
|||
|
||||
// While this type is not a JS type, it is here for convenience so we don't need
|
||||
// to create a new shared library.
|
||||
typedef ExternalDartReferenceRepType = Object;
|
||||
typedef ExternalDartReferenceRepType<T> = T;
|
||||
|
||||
// JSVoid is just a typedef for void.
|
||||
typedef JSVoidRepType = void;
|
||||
|
|
|
@ -22,7 +22,7 @@ js_types.JSObjectRepType _createObjectLiteral() =>
|
|||
@patch
|
||||
JSObject get globalContext => js_util.globalThis as JSObject;
|
||||
|
||||
/// Helper for working with the [JSAny?] top type in a backend agnostic way.
|
||||
// Helper for working with the JSAny? top type in a backend agnostic way.
|
||||
@patch
|
||||
extension NullableUndefineableJSAnyExtension on JSAny? {
|
||||
// TODO(joshualitt): To support incremental migration of existing users to
|
||||
|
@ -64,14 +64,15 @@ extension JSAnyUtilityExtension on JSAny? {
|
|||
Object? dartify() => js_util.dartify(this);
|
||||
}
|
||||
|
||||
/// Utility extensions for [Object?].
|
||||
// Utility extensions for Object?.
|
||||
@patch
|
||||
extension NullableObjectUtilExtension on Object? {
|
||||
@patch
|
||||
JSAny? jsify() => js_util.jsify(this);
|
||||
}
|
||||
|
||||
/// [JSExportedDartFunction] <-> [Function]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSExportedDartFunction <-> Function
|
||||
@patch
|
||||
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
|
||||
@patch
|
||||
|
@ -93,15 +94,16 @@ extension FunctionToJSExportedDartFunction on Function {
|
|||
'transformed by the interop transformer.');
|
||||
}
|
||||
|
||||
/// Embedded global property for wrapped Dart objects passed via JS interop.
|
||||
///
|
||||
/// This is a Symbol so that different Dart applications don't share Dart
|
||||
/// objects from different Dart runtimes. We expect all [JSBoxedDartObject]s to
|
||||
/// have this Symbol.
|
||||
// Embedded global property for wrapped Dart objects passed via JS interop.
|
||||
//
|
||||
// This is a Symbol so that different Dart applications don't share Dart
|
||||
// objects from different Dart runtimes. We expect all JSBoxedDartObjects to
|
||||
// have this Symbol.
|
||||
final JSSymbol _jsBoxedDartObjectProperty = JSSymbol._(JSValue(
|
||||
js_helper.JS<WasmExternRef?>('() => Symbol("jsBoxedDartObjectProperty")')));
|
||||
|
||||
/// [JSBoxedDartObject] <-> [Object]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSBoxedDartObject <-> Object
|
||||
@patch
|
||||
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
|
||||
@patch
|
||||
|
@ -130,22 +132,36 @@ extension ObjectToJSBoxedDartObject on Object {
|
|||
}
|
||||
}
|
||||
|
||||
/// [ExternalDartReference] <-> [Object]
|
||||
// -----------------------------------------------------------------------------
|
||||
// ExternalDartReference <-> Object
|
||||
@patch
|
||||
extension ExternalDartReferenceToObject on ExternalDartReference {
|
||||
extension ExternalDartReferenceToObject<T extends Object?>
|
||||
on ExternalDartReference<T> {
|
||||
@patch
|
||||
Object get toDartObject =>
|
||||
jsObjectToDartObject(this._externalDartReference.toExternRef);
|
||||
T get toDartObject {
|
||||
// TODO(srujzs): We could do an `unsafeCast` here for performance, but
|
||||
// that can result in unsoundness for users. Alternatively, we can
|
||||
// introduce a generic version of `JSValue` which would allow us to safely
|
||||
// `unsafeCast`. However, this has its own issues where a user can't
|
||||
// do casts like `ExternalDartReference<Object> as
|
||||
// ExternalDartReference<int>` since `JSValue<Object>` is not a subtype of
|
||||
// `JSValue<int>`, even though it may be valid to do such a cast.
|
||||
final t = this._externalDartReference;
|
||||
return (t == null ? null : jsObjectToDartObject(t.toExternRef)) as T;
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
extension ObjectToExternalDartReference on Object {
|
||||
extension ObjectToExternalDartReference<T extends Object?> on T {
|
||||
@patch
|
||||
ExternalDartReference get toExternalReference =>
|
||||
ExternalDartReference._(JSValue(jsObjectFromDartObject(this)));
|
||||
ExternalDartReference<T> get toExternalReference {
|
||||
final t = this;
|
||||
return ExternalDartReference<T>._(
|
||||
t == null ? null : JSValue(jsObjectFromDartObject(t)));
|
||||
}
|
||||
}
|
||||
|
||||
/// [JSPromise] -> [Future].
|
||||
// JSPromise -> Future
|
||||
@patch
|
||||
extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
||||
@patch
|
||||
|
@ -182,7 +198,8 @@ extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSArrayBuffer] <-> [ByteBuffer]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSArrayBuffer <-> ByteBuffer
|
||||
@patch
|
||||
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
|
||||
@patch
|
||||
|
@ -202,7 +219,8 @@ extension ByteBufferToJSArrayBuffer on ByteBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSDataView] <-> [ByteData]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSDataView <-> ByteData
|
||||
@patch
|
||||
extension JSDataViewToByteData on JSDataView {
|
||||
@patch
|
||||
|
@ -220,7 +238,8 @@ extension ByteDataToJSDataView on ByteData {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSInt8Array] <-> [Int8List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSInt8Array <-> Int8List
|
||||
@patch
|
||||
extension JSInt8ArrayToInt8List on JSInt8Array {
|
||||
@patch
|
||||
|
@ -238,7 +257,8 @@ extension Int8ListToJSInt8Array on Int8List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSUint8Array] <-> [Uint8List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint8Array <-> Uint8List
|
||||
@patch
|
||||
extension JSUint8ArrayToUint8List on JSUint8Array {
|
||||
@patch
|
||||
|
@ -256,7 +276,8 @@ extension Uint8ListToJSUint8Array on Uint8List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSUint8ClampedArray] <-> [Uint8ClampedList]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint8ClampedArray <-> Uint8ClampedList
|
||||
@patch
|
||||
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
|
||||
@patch
|
||||
|
@ -275,7 +296,8 @@ extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSInt16Array] <-> [Int16List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSInt16Array <-> Int16List
|
||||
@patch
|
||||
extension JSInt16ArrayToInt16List on JSInt16Array {
|
||||
@patch
|
||||
|
@ -293,7 +315,8 @@ extension Int16ListToJSInt16Array on Int16List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSUint16Array] <-> [Uint16List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint16Array <-> Uint16List
|
||||
@patch
|
||||
extension JSUint16ArrayToInt16List on JSUint16Array {
|
||||
@patch
|
||||
|
@ -311,7 +334,8 @@ extension Uint16ListToJSInt16Array on Uint16List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSInt32Array] <-> [Int32List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSInt32Array <-> Int32List
|
||||
@patch
|
||||
extension JSInt32ArrayToInt32List on JSInt32Array {
|
||||
@patch
|
||||
|
@ -329,7 +353,8 @@ extension Int32ListToJSInt32Array on Int32List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSUint32Array] <-> [Uint32List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSUint32Array <-> Uint32List
|
||||
@patch
|
||||
extension JSUint32ArrayToUint32List on JSUint32Array {
|
||||
@patch
|
||||
|
@ -347,7 +372,8 @@ extension Uint32ListToJSUint32Array on Uint32List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSFloat32Array] <-> [Float32List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSFloat32Array <-> Float32List
|
||||
@patch
|
||||
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
|
||||
@patch
|
||||
|
@ -366,7 +392,8 @@ extension Float32ListToJSFloat32Array on Float32List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSFloat64Array] <-> [Float64List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSFloat64Array <-> Float64List
|
||||
@patch
|
||||
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
|
||||
@patch
|
||||
|
@ -385,7 +412,8 @@ extension Float64ListToJSFloat64Array on Float64List {
|
|||
}
|
||||
}
|
||||
|
||||
/// [JSArray] <-> [List]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSArray <-> List
|
||||
@patch
|
||||
extension JSArrayToList<T extends JSAny?> on JSArray<T> {
|
||||
@patch
|
||||
|
@ -410,7 +438,8 @@ extension ListToJSArray<T extends JSAny?> on List<T> {
|
|||
_underlyingArray ?? _createJSProxyOfList<T>(this);
|
||||
}
|
||||
|
||||
/// [JSNumber] -> [double] or [int].
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSNumber -> double or int
|
||||
@patch
|
||||
extension JSNumberToNumber on JSNumber {
|
||||
@patch
|
||||
|
@ -434,7 +463,8 @@ extension DoubleToJSNumber on double {
|
|||
JSNumber get toJS => JSNumber._(JSValue(toJSNumber(this)));
|
||||
}
|
||||
|
||||
/// [JSBoolean] <-> [bool]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSBoolean <-> bool
|
||||
@patch
|
||||
extension JSBooleanToBool on JSBoolean {
|
||||
@patch
|
||||
|
@ -447,7 +477,8 @@ extension BoolToJSBoolean on bool {
|
|||
JSBoolean get toJS => JSBoolean._(JSValue(toJSBoolean(this)));
|
||||
}
|
||||
|
||||
/// [JSString] <-> [String]
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSString <-> String
|
||||
@patch
|
||||
extension JSStringToString on JSString {
|
||||
@patch
|
||||
|
@ -575,12 +606,12 @@ class _Symbol {
|
|||
@staticInterop
|
||||
class __ListBackedJSArray {}
|
||||
|
||||
/// Implementation of indexing, `length`, and core handler methods.
|
||||
///
|
||||
/// JavaScript's `Array` methods are similar to Dart's `ListMixin`, because they
|
||||
/// only rely on the implementation of `length` and indexing methods (and
|
||||
/// support for any JS operators like `in` or `delete`).
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#generic_array_methods
|
||||
// Implementation of indexing, `length`, and core handler methods.
|
||||
//
|
||||
// JavaScript's `Array` methods are similar to Dart's `ListMixin`, because they
|
||||
// only rely on the implementation of `length` and indexing methods (and
|
||||
// support for any JS operators like `in` or `delete`).
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#generic_array_methods
|
||||
class _ListBackedJSArray {
|
||||
final List<JSAny?> _list;
|
||||
// The proxy that wraps this list.
|
||||
|
|
|
@ -83,7 +83,7 @@ typedef JSBigIntRepType = js.JSValue;
|
|||
|
||||
// While this type is not a JS type, it is here for convenience so we don't need
|
||||
// to create a new shared library.
|
||||
typedef ExternalDartReferenceRepType = js.JSValue;
|
||||
typedef ExternalDartReferenceRepType<T> = js.JSValue?;
|
||||
|
||||
// JSVoid is just a typedef for void. While we could just use JSUndefined, in
|
||||
// the future we may be able to use this to elide `return`s in JS trampolines.
|
||||
|
|
|
@ -56,9 +56,9 @@ external JSFunction? _jsWeakRefFunction;
|
|||
final bool _supportsWeakRef = _jsWeakRefFunction != null;
|
||||
|
||||
@js_interop.JS('WeakRef')
|
||||
extension type _JSWeakRef._(JSObject _) implements JSObject {
|
||||
external _JSWeakRef(ExternalDartReference target);
|
||||
external ExternalDartReference? deref();
|
||||
extension type _JSWeakRef<T extends Object>._(JSObject _) implements JSObject {
|
||||
external _JSWeakRef(ExternalDartReference<T> target);
|
||||
external ExternalDartReference<T>? deref();
|
||||
}
|
||||
|
||||
@patch
|
||||
|
@ -77,10 +77,10 @@ class WeakReference<T extends Object> {
|
|||
}
|
||||
|
||||
class _WeakReferenceWrapper<T extends Object> implements WeakReference<T> {
|
||||
final _JSWeakRef _jsWeakRef;
|
||||
final _JSWeakRef<T> _jsWeakRef;
|
||||
_WeakReferenceWrapper(T target)
|
||||
: _jsWeakRef = _JSWeakRef(target.toExternalReference);
|
||||
T? get target => _jsWeakRef.deref()?.toDartObject as T?;
|
||||
T? get target => _jsWeakRef.deref()?.toDartObject;
|
||||
}
|
||||
|
||||
class _WeakReferencePolyfill<T extends Object> implements WeakReference<T> {
|
||||
|
@ -93,14 +93,14 @@ external JSFunction? _jsFinalizationRegistry;
|
|||
final bool _supportsFinalizationRegistry = _jsFinalizationRegistry != null;
|
||||
|
||||
@js_interop.JS('FinalizationRegistry')
|
||||
extension type _JSFinalizationRegistry._(JSObject _) implements JSObject {
|
||||
extension type _JSFinalizationRegistry<T>._(JSObject _) implements JSObject {
|
||||
external _JSFinalizationRegistry(JSFunction callback);
|
||||
@js_interop.JS('register')
|
||||
external void registerWithDetach(ExternalDartReference value,
|
||||
ExternalDartReference? peer, ExternalDartReference detach);
|
||||
external void registerWithDetach(ExternalDartReference<Object> value,
|
||||
ExternalDartReference<T> peer, ExternalDartReference<Object> detach);
|
||||
external void register(
|
||||
ExternalDartReference value, ExternalDartReference? peer);
|
||||
external void unregister(ExternalDartReference detach);
|
||||
ExternalDartReference<Object> value, ExternalDartReference<T> peer);
|
||||
external void unregister(ExternalDartReference<Object> detach);
|
||||
}
|
||||
|
||||
@patch
|
||||
|
@ -134,8 +134,8 @@ class _FinalizationRegistryWrapper<T> implements Finalizer<T> {
|
|||
|
||||
_FinalizationRegistryWrapper(void Function(T) callback)
|
||||
: _jsFinalizationRegistry =
|
||||
_JSFinalizationRegistry(((ExternalDartReference? peer) {
|
||||
callback(unsafeCast<T>(peer?.toDartObject));
|
||||
_JSFinalizationRegistry(((ExternalDartReference<T> peer) {
|
||||
callback(peer.toDartObject);
|
||||
}).toJS);
|
||||
|
||||
void attach(Object value, T peer, {Object? detach}) {
|
||||
|
@ -143,10 +143,10 @@ class _FinalizationRegistryWrapper<T> implements Finalizer<T> {
|
|||
if (detach != null) {
|
||||
_checkValidWeakTarget(detach);
|
||||
_jsFinalizationRegistry.registerWithDetach(value.toExternalReference,
|
||||
peer?.toExternalReference, detach.toExternalReference);
|
||||
peer.toExternalReference, detach.toExternalReference);
|
||||
} else {
|
||||
_jsFinalizationRegistry.register(
|
||||
value.toExternalReference, peer?.toExternalReference);
|
||||
value.toExternalReference, peer.toExternalReference);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -274,6 +274,8 @@ extension type JSBigInt._(JSBigIntRepType _jsBigInt) implements JSAny {}
|
|||
/// used directly without any conversions. When compiling to Wasm, an internal
|
||||
/// Wasm function is used to convert the Dart object to an opaque JavaScript
|
||||
/// value, which can later be converted back using another internal function.
|
||||
/// The underlying representation type is nullable, meaning a non-nullable
|
||||
/// [ExternalDartReference] may be `null`.
|
||||
///
|
||||
/// This interface is a faster alternative to [JSBoxedDartObject] by not
|
||||
/// wrapping the Dart object with a JavaScript object. However, unlike
|
||||
|
@ -290,9 +292,9 @@ extension type JSBigInt._(JSBigIntRepType _jsBigInt) implements JSAny {}
|
|||
/// [ExternalDartReference].
|
||||
///
|
||||
/// See [ObjectToExternalDartReference.toExternalReference] to allow an
|
||||
/// arbitrary [Object] to be passed to JavaScript.
|
||||
extension type ExternalDartReference._(
|
||||
ExternalDartReferenceRepType _externalDartReference) implements Object {}
|
||||
/// arbitrary value of type [T] to be passed to JavaScript.
|
||||
extension type ExternalDartReference<T extends Object?>._(
|
||||
ExternalDartReferenceRepType<T> _externalDartReference) {}
|
||||
|
||||
/// JS type equivalent for `undefined` for interop member return types.
|
||||
///
|
||||
|
@ -380,8 +382,8 @@ extension JSAnyUtilityExtension on JSAny? {
|
|||
/// Whether this <code>[JSAny]?</code> is an instance of the JavaScript type
|
||||
/// that is declared by [T].
|
||||
///
|
||||
/// This method uses a combination of null, `typeof`, and `instanceof` checks
|
||||
/// in order to do this check. Use this instead of `is` checks.
|
||||
/// This method uses a combination of `null`, `typeof`, and `instanceof`
|
||||
/// checks in order to do this check. Use this instead of `is` checks.
|
||||
///
|
||||
/// If [T] is a primitive JS type like [JSString], this uses a `typeof` check
|
||||
/// that corresponds to that primitive type like `typeofEquals('string')`.
|
||||
|
@ -405,7 +407,7 @@ extension JSAnyUtilityExtension on JSAny? {
|
|||
/// `JSTypedArray`. As `TypedArray` does not exist as a property in
|
||||
/// JavaScript, this does some prototype checking to make `isA<JSTypedArray>`
|
||||
/// do the right thing. The other exception is `JSAny`. If you do a
|
||||
/// `isA<JSAny>` check, it will only do a null-check.
|
||||
/// `isA<JSAny>` check, it will only do a `null` check.
|
||||
///
|
||||
/// Using this method with a [T] that has an object literal constructor will
|
||||
/// result in an error as you likely want to use [JSObject] instead.
|
||||
|
@ -517,25 +519,27 @@ extension ObjectToJSBoxedDartObject on Object {
|
|||
external JSBoxedDartObject get toJSBox;
|
||||
}
|
||||
|
||||
/// Conversions from [ExternalDartReference] to [Object].
|
||||
extension ExternalDartReferenceToObject on ExternalDartReference {
|
||||
/// The Dart [Object] that this [ExternalDartReference] refers to.
|
||||
/// Conversions from [ExternalDartReference] to the value of type [T].
|
||||
extension ExternalDartReferenceToObject<T extends Object?>
|
||||
on ExternalDartReference<T> {
|
||||
/// The Dart value of type [T] that this [ExternalDartReference] refers to.
|
||||
///
|
||||
/// When compiling to JavaScript, a Dart object is a JavaScript object, and
|
||||
/// therefore this directly returns the Dart object. When compiling to Wasm,
|
||||
/// an internal Wasm function is used to convert the opaque JavaScript value
|
||||
/// to the original Dart object.
|
||||
external Object get toDartObject;
|
||||
external T get toDartObject;
|
||||
}
|
||||
|
||||
/// Conversions from [Object] to [ExternalDartReference].
|
||||
extension ObjectToExternalDartReference on Object {
|
||||
/// An opaque reference to this [Object] which can be passed to JavaScript.
|
||||
/// Conversions from a value of type [T] to [ExternalDartReference].
|
||||
extension ObjectToExternalDartReference<T extends Object?> on T {
|
||||
/// An opaque reference to this value of type [T] which can be passed to
|
||||
/// JavaScript.
|
||||
///
|
||||
/// When compiling to JavaScript, a Dart object is a JavaScript object, and
|
||||
/// therefore this directly returns the Dart object. When compiling to Wasm,
|
||||
/// an internal Wasm function is used to convert the Dart object to an opaque
|
||||
/// JavaScript value.
|
||||
/// JavaScript value. If this value is `null`, returns `null`.
|
||||
///
|
||||
/// A value of type [ExternalDartReference] should be treated as completely
|
||||
/// opaque. It can only be passed around as-is or converted back using
|
||||
|
@ -546,7 +550,7 @@ extension ObjectToExternalDartReference on Object {
|
|||
/// guaranteed to be equal. Therefore, `==` will always return true between
|
||||
/// such [ExternalDartReference]s. However, like JS types, `identical` between
|
||||
/// such values may return different results depending on the compiler.
|
||||
external ExternalDartReference get toExternalReference;
|
||||
external ExternalDartReference<T> get toExternalReference;
|
||||
}
|
||||
|
||||
/// Conversions from [JSPromise] to [Future].
|
||||
|
|
|
@ -151,16 +151,16 @@ extension on ENonInterop {
|
|||
// [web] JS interop type or @Native type from an SDK web library required for 'external' extension members.
|
||||
}
|
||||
|
||||
extension type EExternalDartReference._(ExternalDartReference _) {
|
||||
extension type EExternalDartReference._(ExternalDartReference<Object> _) {
|
||||
external EExternalDartReference();
|
||||
// ^
|
||||
// [web] Extension type member is marked 'external', but the representation type of its extension type is not a valid JS interop type.
|
||||
}
|
||||
|
||||
@JS()
|
||||
extension type EExternalDartReference2._(ExternalDartReference _) {}
|
||||
extension type EExternalDartReference2._(ExternalDartReference<Object> _) {}
|
||||
// ^
|
||||
// [web] Extension type 'EExternalDartReference2' is marked with a '@JS' annotation, but its representation type is not a valid JS interop type: 'ExternalDartReference'.
|
||||
// [web] Extension type 'EExternalDartReference2' is marked with a '@JS' annotation, but its representation type is not a valid JS interop type: 'ExternalDartReference<Object>'.
|
||||
|
||||
extension on EExternalDartReference {
|
||||
external int field;
|
||||
|
|
|
@ -7,30 +7,34 @@
|
|||
import 'dart:js_interop';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:expect/variations.dart';
|
||||
|
||||
const isJSBackend = const bool.fromEnvironment('dart.library.html');
|
||||
final bool isJSBackend = 1 is ExternalDartReference<int>;
|
||||
|
||||
extension type EExternalDartReference(ExternalDartReference _)
|
||||
implements ExternalDartReference {}
|
||||
extension type EExternalDartReference<T>(ExternalDartReference<T> _)
|
||||
implements ExternalDartReference<T> {}
|
||||
|
||||
@JS()
|
||||
external ExternalDartReference externalDartReference;
|
||||
external ExternalDartReference<Object> externalDartReference;
|
||||
|
||||
@JS()
|
||||
external EExternalDartReference eExternalDartReference;
|
||||
external EExternalDartReference<DartClass> eExternalDartReference;
|
||||
|
||||
@JS('externalDartReference')
|
||||
external ExternalDartReference? nullableExternalDartReference;
|
||||
external ExternalDartReference<Object?> externalDartNullableReference;
|
||||
|
||||
@JS('externalDartReference')
|
||||
external ExternalDartReference<Object>? nullableExternalDartReference;
|
||||
|
||||
// Use a function so that we can use a type parameter that extends an
|
||||
// `ExternalDartReference` type.
|
||||
@JS('identity')
|
||||
external set _identity(JSFunction _);
|
||||
@JS()
|
||||
external T identity<T extends EExternalDartReference>(T t);
|
||||
external T identity<T extends ExternalDartReference?>(T t);
|
||||
|
||||
extension type ObjectLiteral(JSObject _) {
|
||||
external void operator []=(String key, ExternalDartReference value);
|
||||
external void operator []=(String key, ExternalDartReference<Object> value);
|
||||
}
|
||||
|
||||
class DartClass {
|
||||
|
@ -39,23 +43,37 @@ class DartClass {
|
|||
DartClass(this.field);
|
||||
}
|
||||
|
||||
void main() {
|
||||
final dartObject = DartClass(42);
|
||||
class DartSubclass extends DartClass {
|
||||
DartSubclass(super.field);
|
||||
}
|
||||
|
||||
void generalTest() {
|
||||
var dartObject = DartClass(42);
|
||||
|
||||
// `Object` test.
|
||||
externalDartReference = dartObject.toExternalReference;
|
||||
Expect.equals(dartObject, externalDartReference.toDartObject as DartClass);
|
||||
Expect.isTrue(
|
||||
identical(dartObject, externalDartReference.toDartObject as DartClass));
|
||||
eExternalDartReference = EExternalDartReference(externalDartReference);
|
||||
Expect.equals(dartObject, eExternalDartReference.toDartObject as DartClass);
|
||||
Expect.isTrue(
|
||||
identical(dartObject, eExternalDartReference.toDartObject as DartClass));
|
||||
_identity = ((ExternalDartReference e) => e).toJS;
|
||||
Expect.identical(dartObject, externalDartReference.toDartObject as DartClass);
|
||||
|
||||
// Generic test.
|
||||
var externalDartClassReference = dartObject.toExternalReference;
|
||||
Expect.equals(dartObject, externalDartClassReference.toDartObject);
|
||||
Expect.identical(dartObject, externalDartClassReference.toDartObject);
|
||||
// Ensure we get assignability.
|
||||
dartObject = externalDartClassReference.toDartObject;
|
||||
|
||||
// Check that we do the right thing with extension types on
|
||||
// `ExternalDartReference`.
|
||||
eExternalDartReference = EExternalDartReference(externalDartClassReference);
|
||||
Expect.equals(dartObject, eExternalDartReference.toDartObject);
|
||||
Expect.identical(dartObject, eExternalDartReference.toDartObject);
|
||||
|
||||
// Check that `ExternalDartReference` can be used as a parameter and return
|
||||
// type for `Function.toJS`'d functions.
|
||||
_identity = ((ExternalDartReference<DartClass> e) => e).toJS;
|
||||
final externalDartReferenceTypeParam = identity(eExternalDartReference);
|
||||
Expect.equals(
|
||||
dartObject, externalDartReferenceTypeParam.toDartObject as DartClass);
|
||||
Expect.isTrue(identical(
|
||||
dartObject, externalDartReferenceTypeParam.toDartObject as DartClass));
|
||||
Expect.equals(dartObject, externalDartReferenceTypeParam.toDartObject);
|
||||
Expect.identical(dartObject, externalDartReferenceTypeParam.toDartObject);
|
||||
|
||||
// Multiple invocations should return the same underlying value, which is
|
||||
// tested by `==`.
|
||||
|
@ -63,27 +81,111 @@ void main() {
|
|||
// However, they may or may not be identical depending on the compiler due to
|
||||
// dart2wasm wrapping values with new JSValue instances.
|
||||
if (isJSBackend) {
|
||||
Expect.isTrue(
|
||||
identical(externalDartReference, dartObject.toExternalReference));
|
||||
Expect.identical(externalDartReference, dartObject.toExternalReference);
|
||||
} else {
|
||||
Expect.isFalse(
|
||||
identical(externalDartReference, dartObject.toExternalReference));
|
||||
}
|
||||
|
||||
final jsString = ''.toJS;
|
||||
// We don't validate that the input is a Dart object or a JS value as that may
|
||||
// be expensive to validate. We end up externalizing the JSValue wrapper in
|
||||
// this case.
|
||||
final jsString = ''.toJS;
|
||||
Expect.equals(jsString.toExternalReference.toDartObject, jsString);
|
||||
Expect.isTrue(identical(jsString.toExternalReference.toDartObject, jsString));
|
||||
Expect.identical(jsString.toExternalReference.toDartObject, jsString);
|
||||
|
||||
// Check that we do the right thing with nullability still.
|
||||
nullableExternalDartReference = null;
|
||||
if (hasSoundNullSafety) Expect.throws(() => externalDartReference);
|
||||
// Check that the type is checked when internalized for soundness.
|
||||
externalDartReference = dartObject.toExternalReference;
|
||||
Expect.throws(
|
||||
() => (externalDartReference as ExternalDartReference<DartSubclass>)
|
||||
// The cast is deferred until `toDartObject` for dart2wasm, so call it
|
||||
// explicitly.
|
||||
.toDartObject);
|
||||
|
||||
_identity = ((ExternalDartReference<DartSubclass> et) =>
|
||||
et.toDartObject.toExternalReference).toJS;
|
||||
Expect.throws(() => identity(externalDartReference));
|
||||
|
||||
// Check that we do the right thing with nullability still, both in the type
|
||||
// parameter and outside it.
|
||||
_identity = ((ExternalDartReference<Object> et) =>
|
||||
et.toDartObject.toExternalReference).toJS;
|
||||
nullableExternalDartReference = null?.toExternalReference;
|
||||
Expect.isTrue(nullableExternalDartReference == null);
|
||||
Expect.throwsWhen(
|
||||
!unsoundNullSafety, () => externalDartReference.toDartObject);
|
||||
Expect.throwsWhen(
|
||||
!unsoundNullSafety, () => identity(nullableExternalDartReference));
|
||||
externalDartNullableReference = null.toExternalReference;
|
||||
Expect.isTrue(externalDartNullableReference == null);
|
||||
Expect.throwsWhen(
|
||||
!unsoundNullSafety, () => externalDartReference.toDartObject);
|
||||
Expect.throwsWhen(
|
||||
!unsoundNullSafety, () => identity(externalDartNullableReference));
|
||||
// Check that they're both Dart `null`.
|
||||
Expect.identical(
|
||||
nullableExternalDartReference, externalDartNullableReference);
|
||||
|
||||
// Functions should not trigger `assertInterop`.
|
||||
externalDartReference = () {}.toExternalReference;
|
||||
identity(EExternalDartReference(() {}.toExternalReference));
|
||||
final literal = ObjectLiteral(JSObject());
|
||||
literal['ref'] = externalDartReference;
|
||||
ObjectLiteral(JSObject())['ref'] = externalDartReference;
|
||||
}
|
||||
|
||||
// An example interface for a generic `WritableSignal` from
|
||||
// https://angular.dev/guide/signals that avoids unnecessary casts and wrapper
|
||||
// functions in the JS compilers. The functions are stubbed to just test casts
|
||||
// and assignability.
|
||||
extension type WritableSignal<T>(JSFunction _) {
|
||||
void _set(ExternalDartReference<T> value) {}
|
||||
|
||||
void set(T value) => _set(value.toExternalReference);
|
||||
|
||||
void _update(JSExportedDartFunction update) {}
|
||||
|
||||
void update(T Function(T) function) {
|
||||
// Because `ExternalDartReference<T>`s are `T` on the JS backends, we can
|
||||
// avoid the wrapper function that is needed for dart2wasm. If we want code
|
||||
// that is guaranteed to work on all backends, the wrapper function will
|
||||
// work, but will be slower on the JS backends. See
|
||||
// https://github.com/dart-lang/sdk/issues/55342 for more details.
|
||||
if (isJSBackend) {
|
||||
_update((function as ExternalDartReference<T> Function(
|
||||
ExternalDartReference<T>))
|
||||
.toJS);
|
||||
}
|
||||
// Should work on all backends.
|
||||
_update(((ExternalDartReference<T> e) =>
|
||||
function(e.toDartObject).toExternalReference).toJS);
|
||||
}
|
||||
}
|
||||
|
||||
WritableSignal<T> signal<T>(T initialValue) =>
|
||||
WritableSignal<T>((() => initialValue.toExternalReference).toJS);
|
||||
|
||||
void signalsTest() {
|
||||
final writableSignal = signal<Object?>(0);
|
||||
writableSignal.set(null);
|
||||
writableSignal.set(true);
|
||||
writableSignal.set(DartClass(42));
|
||||
writableSignal.update((Object? x) => x);
|
||||
writableSignal.update((_) => false);
|
||||
writableSignal.update((Object? _) => null);
|
||||
final writableIntSignal = signal(null as int?);
|
||||
writableIntSignal.set(null);
|
||||
writableIntSignal.set(0);
|
||||
writableIntSignal.update((int? x) => x);
|
||||
writableIntSignal.update((int? _) => null);
|
||||
writableIntSignal.update((num? _) => 0);
|
||||
final writableDartSignal = signal<DartClass?>(DartClass(42));
|
||||
writableDartSignal.set(null);
|
||||
writableDartSignal.set(DartSubclass(42));
|
||||
writableDartSignal.update((DartClass? x) => x);
|
||||
writableDartSignal.update((DartClass? x) => x as DartSubclass);
|
||||
writableDartSignal.update((Object? _) => DartClass(42));
|
||||
}
|
||||
|
||||
void main() {
|
||||
generalTest();
|
||||
signalsTest();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue