mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:35:05 +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.
|
of a `String` to support other JS values as well, like `TrustedScriptURL`s.
|
||||||
- **Breaking Change** [#55267][]: `isTruthy` and `not` now return `JSBoolean`
|
- **Breaking Change** [#55267][]: `isTruthy` and `not` now return `JSBoolean`
|
||||||
instead of `bool` to be consistent with the other operators.
|
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
|
[#55508]: https://github.com/dart-lang/sdk/issues/55508
|
||||||
[#55267]: https://github.com/dart-lang/sdk/issues/55267
|
[#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
|
### Tools
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class CallbackSpecializer {
|
||||||
VariableGet v = VariableGet(positionalParameters[i]);
|
VariableGet v = VariableGet(positionalParameters[i]);
|
||||||
if (_util.isJSValueType(callbackParameterType) && boxExternRef) {
|
if (_util.isJSValueType(callbackParameterType) && boxExternRef) {
|
||||||
expression = _createJSValue(v);
|
expression = _createJSValue(v);
|
||||||
if (!callbackParameterType.isPotentiallyNullable) {
|
if (!callbackParameterType.extensionTypeErasure.isPotentiallyNullable) {
|
||||||
expression = NullCheck(expression);
|
expression = NullCheck(expression);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -70,7 +70,7 @@ class CoreTypesUtil {
|
||||||
Procedure jsifyTarget(DartType type) =>
|
Procedure jsifyTarget(DartType type) =>
|
||||||
isJSValueType(type) ? jsValueUnboxTarget : jsifyRawTarget;
|
isJSValueType(type) ? jsValueUnboxTarget : jsifyRawTarget;
|
||||||
|
|
||||||
/// Return whether [type] erases to a `JSValue`.
|
/// Whether [type] erases to a `JSValue` or `JSValue?`.
|
||||||
bool isJSValueType(DartType type) =>
|
bool isJSValueType(DartType type) =>
|
||||||
_extensionIndex.isStaticInteropType(type) ||
|
_extensionIndex.isStaticInteropType(type) ||
|
||||||
_extensionIndex.isExternalDartReferenceType(type);
|
_extensionIndex.isExternalDartReferenceType(type);
|
||||||
|
@ -112,7 +112,7 @@ class CoreTypesUtil {
|
||||||
// there are static interop types that are not boxed as JSValue, we
|
// there are static interop types that are not boxed as JSValue, we
|
||||||
// might need a proper cast then.
|
// might need a proper cast then.
|
||||||
expression = invokeOneArg(jsValueBoxTarget, invocation);
|
expression = invokeOneArg(jsValueBoxTarget, invocation);
|
||||||
if (returnType.isPotentiallyNonNullable) {
|
if (returnType.extensionTypeErasure.isPotentiallyNonNullable) {
|
||||||
expression = NullCheck(expression);
|
expression = NullCheck(expression);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,7 +19,7 @@ JSObjectRepType _createObjectLiteral() =>
|
||||||
@pragma('dart2js:prefer-inline')
|
@pragma('dart2js:prefer-inline')
|
||||||
JSObject get globalContext => staticInteropGlobalContext as JSObject;
|
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
|
@patch
|
||||||
extension NullableUndefineableJSAnyExtension on JSAny? {
|
extension NullableUndefineableJSAnyExtension on JSAny? {
|
||||||
@patch
|
@patch
|
||||||
|
@ -53,7 +53,7 @@ extension JSAnyUtilityExtension on JSAny? {
|
||||||
Object? dartify() => js_util.dartify(this);
|
Object? dartify() => js_util.dartify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility extensions for [Object?].
|
// Utility extensions for Object?.
|
||||||
@patch
|
@patch
|
||||||
extension NullableObjectUtilExtension on Object? {
|
extension NullableObjectUtilExtension on Object? {
|
||||||
@patch
|
@patch
|
||||||
|
@ -61,7 +61,8 @@ extension NullableObjectUtilExtension on Object? {
|
||||||
JSAny? jsify() => js_util.jsify(this);
|
JSAny? jsify() => js_util.jsify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSExportedDartFunction] <-> [Function]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSExportedDartFunction <-> Function
|
||||||
@patch
|
@patch
|
||||||
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
|
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
|
||||||
// TODO(srujzs): We should unwrap rather than allow arbitrary JS functions
|
// 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;
|
js_util.allowInterop(this) as JSExportedDartFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Embedded global property for wrapped Dart objects passed via JS interop.
|
// Embedded global property for wrapped Dart objects passed via JS interop.
|
||||||
///
|
//
|
||||||
/// This is a Symbol so that different Dart applications don't share Dart
|
// This is a Symbol so that different Dart applications don't share Dart
|
||||||
/// objects from different Dart runtimes. We expect all [JSBoxedDartObject]s to
|
// objects from different Dart runtimes. We expect all JSBoxedDartObjects to
|
||||||
/// have this Symbol.
|
// have this Symbol.
|
||||||
final Object _jsBoxedDartObjectProperty =
|
final Object _jsBoxedDartObjectProperty =
|
||||||
foreign_helper.JS('', 'Symbol("jsBoxedDartObjectProperty")');
|
foreign_helper.JS('', 'Symbol("jsBoxedDartObjectProperty")');
|
||||||
|
|
||||||
/// [JSBoxedDartObject] <-> [Object]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSBoxedDartObject <-> Object
|
||||||
@patch
|
@patch
|
||||||
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
|
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
|
||||||
@patch
|
@patch
|
||||||
|
@ -118,23 +120,25 @@ extension ObjectToJSBoxedDartObject on Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [ExternalDartReference] <-> [Object]
|
// -----------------------------------------------------------------------------
|
||||||
|
// ExternalDartReference <-> T
|
||||||
@patch
|
@patch
|
||||||
extension ExternalDartReferenceToObject on ExternalDartReference {
|
extension ExternalDartReferenceToObject<T extends Object?>
|
||||||
|
on ExternalDartReference<T> {
|
||||||
@patch
|
@patch
|
||||||
@pragma('dart2js:prefer-inline')
|
@pragma('dart2js:prefer-inline')
|
||||||
Object get toDartObject => this;
|
T get toDartObject => _externalDartReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
extension ObjectToExternalDartReference on Object {
|
extension ObjectToExternalDartReference<T extends Object?> on T {
|
||||||
@patch
|
@patch
|
||||||
@pragma('dart2js:prefer-inline')
|
@pragma('dart2js:prefer-inline')
|
||||||
ExternalDartReference get toExternalReference =>
|
ExternalDartReference<T> get toExternalReference =>
|
||||||
ExternalDartReference._(this);
|
ExternalDartReference<T>._(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSPromise] -> [Future].
|
// JSPromise -> Future
|
||||||
@patch
|
@patch
|
||||||
extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
||||||
@patch
|
@patch
|
||||||
|
@ -142,7 +146,8 @@ extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
||||||
Future<T> get toDart => js_util.promiseToFuture<T>(this);
|
Future<T> get toDart => js_util.promiseToFuture<T>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSArrayBuffer] <-> [ByteBuffer]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSArrayBuffer <-> ByteBuffer
|
||||||
@patch
|
@patch
|
||||||
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
|
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
|
||||||
@patch
|
@patch
|
||||||
|
@ -157,7 +162,8 @@ extension ByteBufferToJSArrayBuffer on ByteBuffer {
|
||||||
JSArrayBuffer get toJS => this as JSArrayBuffer;
|
JSArrayBuffer get toJS => this as JSArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSDataView] <-> [ByteData]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSDataView <-> ByteData
|
||||||
@patch
|
@patch
|
||||||
extension JSDataViewToByteData on JSDataView {
|
extension JSDataViewToByteData on JSDataView {
|
||||||
@patch
|
@patch
|
||||||
|
@ -172,7 +178,8 @@ extension ByteDataToJSDataView on ByteData {
|
||||||
JSDataView get toJS => this as JSDataView;
|
JSDataView get toJS => this as JSDataView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSInt8Array] <-> [Int8List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSInt8Array <-> Int8List
|
||||||
@patch
|
@patch
|
||||||
extension JSInt8ArrayToInt8List on JSInt8Array {
|
extension JSInt8ArrayToInt8List on JSInt8Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -187,7 +194,8 @@ extension Int8ListToJSInt8Array on Int8List {
|
||||||
JSInt8Array get toJS => this as JSInt8Array;
|
JSInt8Array get toJS => this as JSInt8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint8Array] <-> [Uint8List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint8Array <-> Uint8List
|
||||||
@patch
|
@patch
|
||||||
extension JSUint8ArrayToUint8List on JSUint8Array {
|
extension JSUint8ArrayToUint8List on JSUint8Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -202,7 +210,8 @@ extension Uint8ListToJSUint8Array on Uint8List {
|
||||||
JSUint8Array get toJS => this as JSUint8Array;
|
JSUint8Array get toJS => this as JSUint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint8ClampedArray] <-> [Uint8ClampedList]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint8ClampedArray <-> Uint8ClampedList
|
||||||
@patch
|
@patch
|
||||||
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
|
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
|
||||||
@patch
|
@patch
|
||||||
|
@ -217,7 +226,8 @@ extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList {
|
||||||
JSUint8ClampedArray get toJS => this as JSUint8ClampedArray;
|
JSUint8ClampedArray get toJS => this as JSUint8ClampedArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSInt16Array] <-> [Int16List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSInt16Array <-> Int16List
|
||||||
@patch
|
@patch
|
||||||
extension JSInt16ArrayToInt16List on JSInt16Array {
|
extension JSInt16ArrayToInt16List on JSInt16Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -232,7 +242,8 @@ extension Int16ListToJSInt16Array on Int16List {
|
||||||
JSInt16Array get toJS => this as JSInt16Array;
|
JSInt16Array get toJS => this as JSInt16Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint16Array] <-> [Uint16List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint16Array <-> Uint16List
|
||||||
@patch
|
@patch
|
||||||
extension JSUint16ArrayToInt16List on JSUint16Array {
|
extension JSUint16ArrayToInt16List on JSUint16Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -247,7 +258,8 @@ extension Uint16ListToJSInt16Array on Uint16List {
|
||||||
JSUint16Array get toJS => this as JSUint16Array;
|
JSUint16Array get toJS => this as JSUint16Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSInt32Array] <-> [Int32List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSInt32Array <-> Int32List
|
||||||
@patch
|
@patch
|
||||||
extension JSInt32ArrayToInt32List on JSInt32Array {
|
extension JSInt32ArrayToInt32List on JSInt32Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -262,7 +274,8 @@ extension Int32ListToJSInt32Array on Int32List {
|
||||||
JSInt32Array get toJS => this as JSInt32Array;
|
JSInt32Array get toJS => this as JSInt32Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint32Array] <-> [Uint32List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint32Array <-> Uint32List
|
||||||
@patch
|
@patch
|
||||||
extension JSUint32ArrayToUint32List on JSUint32Array {
|
extension JSUint32ArrayToUint32List on JSUint32Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -277,7 +290,8 @@ extension Uint32ListToJSUint32Array on Uint32List {
|
||||||
JSUint32Array get toJS => this as JSUint32Array;
|
JSUint32Array get toJS => this as JSUint32Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSFloat32Array] <-> [Float32List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSFloat32Array <-> Float32List
|
||||||
@patch
|
@patch
|
||||||
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
|
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -292,7 +306,8 @@ extension Float32ListToJSFloat32Array on Float32List {
|
||||||
JSFloat32Array get toJS => this as JSFloat32Array;
|
JSFloat32Array get toJS => this as JSFloat32Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSFloat64Array] <-> [Float64List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSFloat64Array <-> Float64List
|
||||||
@patch
|
@patch
|
||||||
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
|
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -307,7 +322,8 @@ extension Float64ListToJSFloat64Array on Float64List {
|
||||||
JSFloat64Array get toJS => this as JSFloat64Array;
|
JSFloat64Array get toJS => this as JSFloat64Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSArray] <-> [List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSArray <-> List
|
||||||
@patch
|
@patch
|
||||||
extension JSArrayToList<T extends JSAny?> on JSArray<T> {
|
extension JSArrayToList<T extends JSAny?> on JSArray<T> {
|
||||||
@patch
|
@patch
|
||||||
|
@ -339,7 +355,8 @@ extension ListToJSArray<T extends JSAny?> on List<T> {
|
||||||
JSArray<T> get toJSProxyOrRef => this as JSArray<T>;
|
JSArray<T> get toJSProxyOrRef => this as JSArray<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSNumber] -> [double] or [int].
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSNumber -> double or int
|
||||||
@patch
|
@patch
|
||||||
extension JSNumberToNumber on JSNumber {
|
extension JSNumberToNumber on JSNumber {
|
||||||
@patch
|
@patch
|
||||||
|
@ -351,7 +368,8 @@ extension JSNumberToNumber on JSNumber {
|
||||||
int get toDartInt => this as int;
|
int get toDartInt => this as int;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [double] -> [JSNumber].
|
// -----------------------------------------------------------------------------
|
||||||
|
// double -> JSNumber
|
||||||
@patch
|
@patch
|
||||||
extension DoubleToJSNumber on double {
|
extension DoubleToJSNumber on double {
|
||||||
@patch
|
@patch
|
||||||
|
@ -359,7 +377,8 @@ extension DoubleToJSNumber on double {
|
||||||
JSNumber get toJS => JSNumber._(this);
|
JSNumber get toJS => JSNumber._(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSBoolean] <-> [bool]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSBoolean <-> bool
|
||||||
@patch
|
@patch
|
||||||
extension JSBooleanToBool on JSBoolean {
|
extension JSBooleanToBool on JSBoolean {
|
||||||
@patch
|
@patch
|
||||||
|
@ -374,7 +393,8 @@ extension BoolToJSBoolean on bool {
|
||||||
JSBoolean get toJS => JSBoolean._(this);
|
JSBoolean get toJS => JSBoolean._(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSString] <-> [String]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSString <-> String
|
||||||
@patch
|
@patch
|
||||||
extension JSStringToString on JSString {
|
extension JSStringToString on JSString {
|
||||||
@patch
|
@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
|
// While this type is not a JS type, it is here for convenience so we don't need
|
||||||
// to create a new shared library.
|
// to create a new shared library.
|
||||||
typedef ExternalDartReferenceRepType = Object;
|
typedef ExternalDartReferenceRepType<T> = T;
|
||||||
|
|
||||||
// JSVoid is just a typedef for void.
|
// JSVoid is just a typedef for void.
|
||||||
typedef JSVoidRepType = void;
|
typedef JSVoidRepType = void;
|
||||||
|
|
|
@ -22,7 +22,7 @@ js_types.JSObjectRepType _createObjectLiteral() =>
|
||||||
@patch
|
@patch
|
||||||
JSObject get globalContext => js_util.globalThis as JSObject;
|
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
|
@patch
|
||||||
extension NullableUndefineableJSAnyExtension on JSAny? {
|
extension NullableUndefineableJSAnyExtension on JSAny? {
|
||||||
// TODO(joshualitt): To support incremental migration of existing users to
|
// TODO(joshualitt): To support incremental migration of existing users to
|
||||||
|
@ -64,14 +64,15 @@ extension JSAnyUtilityExtension on JSAny? {
|
||||||
Object? dartify() => js_util.dartify(this);
|
Object? dartify() => js_util.dartify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility extensions for [Object?].
|
// Utility extensions for Object?.
|
||||||
@patch
|
@patch
|
||||||
extension NullableObjectUtilExtension on Object? {
|
extension NullableObjectUtilExtension on Object? {
|
||||||
@patch
|
@patch
|
||||||
JSAny? jsify() => js_util.jsify(this);
|
JSAny? jsify() => js_util.jsify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSExportedDartFunction] <-> [Function]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSExportedDartFunction <-> Function
|
||||||
@patch
|
@patch
|
||||||
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
|
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
|
||||||
@patch
|
@patch
|
||||||
|
@ -93,15 +94,16 @@ extension FunctionToJSExportedDartFunction on Function {
|
||||||
'transformed by the interop transformer.');
|
'transformed by the interop transformer.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Embedded global property for wrapped Dart objects passed via JS interop.
|
// Embedded global property for wrapped Dart objects passed via JS interop.
|
||||||
///
|
//
|
||||||
/// This is a Symbol so that different Dart applications don't share Dart
|
// This is a Symbol so that different Dart applications don't share Dart
|
||||||
/// objects from different Dart runtimes. We expect all [JSBoxedDartObject]s to
|
// objects from different Dart runtimes. We expect all JSBoxedDartObjects to
|
||||||
/// have this Symbol.
|
// have this Symbol.
|
||||||
final JSSymbol _jsBoxedDartObjectProperty = JSSymbol._(JSValue(
|
final JSSymbol _jsBoxedDartObjectProperty = JSSymbol._(JSValue(
|
||||||
js_helper.JS<WasmExternRef?>('() => Symbol("jsBoxedDartObjectProperty")')));
|
js_helper.JS<WasmExternRef?>('() => Symbol("jsBoxedDartObjectProperty")')));
|
||||||
|
|
||||||
/// [JSBoxedDartObject] <-> [Object]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSBoxedDartObject <-> Object
|
||||||
@patch
|
@patch
|
||||||
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
|
extension JSBoxedDartObjectToObject on JSBoxedDartObject {
|
||||||
@patch
|
@patch
|
||||||
|
@ -130,22 +132,36 @@ extension ObjectToJSBoxedDartObject on Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [ExternalDartReference] <-> [Object]
|
// -----------------------------------------------------------------------------
|
||||||
|
// ExternalDartReference <-> Object
|
||||||
@patch
|
@patch
|
||||||
extension ExternalDartReferenceToObject on ExternalDartReference {
|
extension ExternalDartReferenceToObject<T extends Object?>
|
||||||
|
on ExternalDartReference<T> {
|
||||||
@patch
|
@patch
|
||||||
Object get toDartObject =>
|
T get toDartObject {
|
||||||
jsObjectToDartObject(this._externalDartReference.toExternRef);
|
// 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
|
@patch
|
||||||
extension ObjectToExternalDartReference on Object {
|
extension ObjectToExternalDartReference<T extends Object?> on T {
|
||||||
@patch
|
@patch
|
||||||
ExternalDartReference get toExternalReference =>
|
ExternalDartReference<T> get toExternalReference {
|
||||||
ExternalDartReference._(JSValue(jsObjectFromDartObject(this)));
|
final t = this;
|
||||||
|
return ExternalDartReference<T>._(
|
||||||
|
t == null ? null : JSValue(jsObjectFromDartObject(t)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSPromise] -> [Future].
|
// JSPromise -> Future
|
||||||
@patch
|
@patch
|
||||||
extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
||||||
@patch
|
@patch
|
||||||
|
@ -182,7 +198,8 @@ extension JSPromiseToFuture<T extends JSAny?> on JSPromise<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSArrayBuffer] <-> [ByteBuffer]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSArrayBuffer <-> ByteBuffer
|
||||||
@patch
|
@patch
|
||||||
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
|
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
|
||||||
@patch
|
@patch
|
||||||
|
@ -202,7 +219,8 @@ extension ByteBufferToJSArrayBuffer on ByteBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSDataView] <-> [ByteData]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSDataView <-> ByteData
|
||||||
@patch
|
@patch
|
||||||
extension JSDataViewToByteData on JSDataView {
|
extension JSDataViewToByteData on JSDataView {
|
||||||
@patch
|
@patch
|
||||||
|
@ -220,7 +238,8 @@ extension ByteDataToJSDataView on ByteData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSInt8Array] <-> [Int8List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSInt8Array <-> Int8List
|
||||||
@patch
|
@patch
|
||||||
extension JSInt8ArrayToInt8List on JSInt8Array {
|
extension JSInt8ArrayToInt8List on JSInt8Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -238,7 +257,8 @@ extension Int8ListToJSInt8Array on Int8List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint8Array] <-> [Uint8List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint8Array <-> Uint8List
|
||||||
@patch
|
@patch
|
||||||
extension JSUint8ArrayToUint8List on JSUint8Array {
|
extension JSUint8ArrayToUint8List on JSUint8Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -256,7 +276,8 @@ extension Uint8ListToJSUint8Array on Uint8List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint8ClampedArray] <-> [Uint8ClampedList]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint8ClampedArray <-> Uint8ClampedList
|
||||||
@patch
|
@patch
|
||||||
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
|
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
|
||||||
@patch
|
@patch
|
||||||
|
@ -275,7 +296,8 @@ extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSInt16Array] <-> [Int16List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSInt16Array <-> Int16List
|
||||||
@patch
|
@patch
|
||||||
extension JSInt16ArrayToInt16List on JSInt16Array {
|
extension JSInt16ArrayToInt16List on JSInt16Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -293,7 +315,8 @@ extension Int16ListToJSInt16Array on Int16List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint16Array] <-> [Uint16List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint16Array <-> Uint16List
|
||||||
@patch
|
@patch
|
||||||
extension JSUint16ArrayToInt16List on JSUint16Array {
|
extension JSUint16ArrayToInt16List on JSUint16Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -311,7 +334,8 @@ extension Uint16ListToJSInt16Array on Uint16List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSInt32Array] <-> [Int32List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSInt32Array <-> Int32List
|
||||||
@patch
|
@patch
|
||||||
extension JSInt32ArrayToInt32List on JSInt32Array {
|
extension JSInt32ArrayToInt32List on JSInt32Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -329,7 +353,8 @@ extension Int32ListToJSInt32Array on Int32List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSUint32Array] <-> [Uint32List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSUint32Array <-> Uint32List
|
||||||
@patch
|
@patch
|
||||||
extension JSUint32ArrayToUint32List on JSUint32Array {
|
extension JSUint32ArrayToUint32List on JSUint32Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -347,7 +372,8 @@ extension Uint32ListToJSUint32Array on Uint32List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSFloat32Array] <-> [Float32List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSFloat32Array <-> Float32List
|
||||||
@patch
|
@patch
|
||||||
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
|
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -366,7 +392,8 @@ extension Float32ListToJSFloat32Array on Float32List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSFloat64Array] <-> [Float64List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSFloat64Array <-> Float64List
|
||||||
@patch
|
@patch
|
||||||
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
|
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
|
||||||
@patch
|
@patch
|
||||||
|
@ -385,7 +412,8 @@ extension Float64ListToJSFloat64Array on Float64List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSArray] <-> [List]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSArray <-> List
|
||||||
@patch
|
@patch
|
||||||
extension JSArrayToList<T extends JSAny?> on JSArray<T> {
|
extension JSArrayToList<T extends JSAny?> on JSArray<T> {
|
||||||
@patch
|
@patch
|
||||||
|
@ -410,7 +438,8 @@ extension ListToJSArray<T extends JSAny?> on List<T> {
|
||||||
_underlyingArray ?? _createJSProxyOfList<T>(this);
|
_underlyingArray ?? _createJSProxyOfList<T>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSNumber] -> [double] or [int].
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSNumber -> double or int
|
||||||
@patch
|
@patch
|
||||||
extension JSNumberToNumber on JSNumber {
|
extension JSNumberToNumber on JSNumber {
|
||||||
@patch
|
@patch
|
||||||
|
@ -434,7 +463,8 @@ extension DoubleToJSNumber on double {
|
||||||
JSNumber get toJS => JSNumber._(JSValue(toJSNumber(this)));
|
JSNumber get toJS => JSNumber._(JSValue(toJSNumber(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSBoolean] <-> [bool]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSBoolean <-> bool
|
||||||
@patch
|
@patch
|
||||||
extension JSBooleanToBool on JSBoolean {
|
extension JSBooleanToBool on JSBoolean {
|
||||||
@patch
|
@patch
|
||||||
|
@ -447,7 +477,8 @@ extension BoolToJSBoolean on bool {
|
||||||
JSBoolean get toJS => JSBoolean._(JSValue(toJSBoolean(this)));
|
JSBoolean get toJS => JSBoolean._(JSValue(toJSBoolean(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [JSString] <-> [String]
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSString <-> String
|
||||||
@patch
|
@patch
|
||||||
extension JSStringToString on JSString {
|
extension JSStringToString on JSString {
|
||||||
@patch
|
@patch
|
||||||
|
@ -575,12 +606,12 @@ class _Symbol {
|
||||||
@staticInterop
|
@staticInterop
|
||||||
class __ListBackedJSArray {}
|
class __ListBackedJSArray {}
|
||||||
|
|
||||||
/// Implementation of indexing, `length`, and core handler methods.
|
// Implementation of indexing, `length`, and core handler methods.
|
||||||
///
|
//
|
||||||
/// JavaScript's `Array` methods are similar to Dart's `ListMixin`, because they
|
// JavaScript's `Array` methods are similar to Dart's `ListMixin`, because they
|
||||||
/// only rely on the implementation of `length` and indexing methods (and
|
// only rely on the implementation of `length` and indexing methods (and
|
||||||
/// support for any JS operators like `in` or `delete`).
|
// 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
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#generic_array_methods
|
||||||
class _ListBackedJSArray {
|
class _ListBackedJSArray {
|
||||||
final List<JSAny?> _list;
|
final List<JSAny?> _list;
|
||||||
// The proxy that wraps this 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
|
// While this type is not a JS type, it is here for convenience so we don't need
|
||||||
// to create a new shared library.
|
// 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
|
// 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.
|
// 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;
|
final bool _supportsWeakRef = _jsWeakRefFunction != null;
|
||||||
|
|
||||||
@js_interop.JS('WeakRef')
|
@js_interop.JS('WeakRef')
|
||||||
extension type _JSWeakRef._(JSObject _) implements JSObject {
|
extension type _JSWeakRef<T extends Object>._(JSObject _) implements JSObject {
|
||||||
external _JSWeakRef(ExternalDartReference target);
|
external _JSWeakRef(ExternalDartReference<T> target);
|
||||||
external ExternalDartReference? deref();
|
external ExternalDartReference<T>? deref();
|
||||||
}
|
}
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
|
@ -77,10 +77,10 @@ class WeakReference<T extends Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _WeakReferenceWrapper<T extends Object> implements WeakReference<T> {
|
class _WeakReferenceWrapper<T extends Object> implements WeakReference<T> {
|
||||||
final _JSWeakRef _jsWeakRef;
|
final _JSWeakRef<T> _jsWeakRef;
|
||||||
_WeakReferenceWrapper(T target)
|
_WeakReferenceWrapper(T target)
|
||||||
: _jsWeakRef = _JSWeakRef(target.toExternalReference);
|
: _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> {
|
class _WeakReferencePolyfill<T extends Object> implements WeakReference<T> {
|
||||||
|
@ -93,14 +93,14 @@ external JSFunction? _jsFinalizationRegistry;
|
||||||
final bool _supportsFinalizationRegistry = _jsFinalizationRegistry != null;
|
final bool _supportsFinalizationRegistry = _jsFinalizationRegistry != null;
|
||||||
|
|
||||||
@js_interop.JS('FinalizationRegistry')
|
@js_interop.JS('FinalizationRegistry')
|
||||||
extension type _JSFinalizationRegistry._(JSObject _) implements JSObject {
|
extension type _JSFinalizationRegistry<T>._(JSObject _) implements JSObject {
|
||||||
external _JSFinalizationRegistry(JSFunction callback);
|
external _JSFinalizationRegistry(JSFunction callback);
|
||||||
@js_interop.JS('register')
|
@js_interop.JS('register')
|
||||||
external void registerWithDetach(ExternalDartReference value,
|
external void registerWithDetach(ExternalDartReference<Object> value,
|
||||||
ExternalDartReference? peer, ExternalDartReference detach);
|
ExternalDartReference<T> peer, ExternalDartReference<Object> detach);
|
||||||
external void register(
|
external void register(
|
||||||
ExternalDartReference value, ExternalDartReference? peer);
|
ExternalDartReference<Object> value, ExternalDartReference<T> peer);
|
||||||
external void unregister(ExternalDartReference detach);
|
external void unregister(ExternalDartReference<Object> detach);
|
||||||
}
|
}
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
|
@ -134,8 +134,8 @@ class _FinalizationRegistryWrapper<T> implements Finalizer<T> {
|
||||||
|
|
||||||
_FinalizationRegistryWrapper(void Function(T) callback)
|
_FinalizationRegistryWrapper(void Function(T) callback)
|
||||||
: _jsFinalizationRegistry =
|
: _jsFinalizationRegistry =
|
||||||
_JSFinalizationRegistry(((ExternalDartReference? peer) {
|
_JSFinalizationRegistry(((ExternalDartReference<T> peer) {
|
||||||
callback(unsafeCast<T>(peer?.toDartObject));
|
callback(peer.toDartObject);
|
||||||
}).toJS);
|
}).toJS);
|
||||||
|
|
||||||
void attach(Object value, T peer, {Object? detach}) {
|
void attach(Object value, T peer, {Object? detach}) {
|
||||||
|
@ -143,10 +143,10 @@ class _FinalizationRegistryWrapper<T> implements Finalizer<T> {
|
||||||
if (detach != null) {
|
if (detach != null) {
|
||||||
_checkValidWeakTarget(detach);
|
_checkValidWeakTarget(detach);
|
||||||
_jsFinalizationRegistry.registerWithDetach(value.toExternalReference,
|
_jsFinalizationRegistry.registerWithDetach(value.toExternalReference,
|
||||||
peer?.toExternalReference, detach.toExternalReference);
|
peer.toExternalReference, detach.toExternalReference);
|
||||||
} else {
|
} else {
|
||||||
_jsFinalizationRegistry.register(
|
_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
|
/// used directly without any conversions. When compiling to Wasm, an internal
|
||||||
/// Wasm function is used to convert the Dart object to an opaque JavaScript
|
/// Wasm function is used to convert the Dart object to an opaque JavaScript
|
||||||
/// value, which can later be converted back using another internal function.
|
/// 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
|
/// This interface is a faster alternative to [JSBoxedDartObject] by not
|
||||||
/// wrapping the Dart object with a JavaScript object. However, unlike
|
/// wrapping the Dart object with a JavaScript object. However, unlike
|
||||||
|
@ -290,9 +292,9 @@ extension type JSBigInt._(JSBigIntRepType _jsBigInt) implements JSAny {}
|
||||||
/// [ExternalDartReference].
|
/// [ExternalDartReference].
|
||||||
///
|
///
|
||||||
/// See [ObjectToExternalDartReference.toExternalReference] to allow an
|
/// See [ObjectToExternalDartReference.toExternalReference] to allow an
|
||||||
/// arbitrary [Object] to be passed to JavaScript.
|
/// arbitrary value of type [T] to be passed to JavaScript.
|
||||||
extension type ExternalDartReference._(
|
extension type ExternalDartReference<T extends Object?>._(
|
||||||
ExternalDartReferenceRepType _externalDartReference) implements Object {}
|
ExternalDartReferenceRepType<T> _externalDartReference) {}
|
||||||
|
|
||||||
/// JS type equivalent for `undefined` for interop member return types.
|
/// 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
|
/// Whether this <code>[JSAny]?</code> is an instance of the JavaScript type
|
||||||
/// that is declared by [T].
|
/// that is declared by [T].
|
||||||
///
|
///
|
||||||
/// This method uses a combination of null, `typeof`, and `instanceof` checks
|
/// This method uses a combination of `null`, `typeof`, and `instanceof`
|
||||||
/// in order to do this check. Use this instead of `is` checks.
|
/// 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
|
/// If [T] is a primitive JS type like [JSString], this uses a `typeof` check
|
||||||
/// that corresponds to that primitive type like `typeofEquals('string')`.
|
/// 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
|
/// `JSTypedArray`. As `TypedArray` does not exist as a property in
|
||||||
/// JavaScript, this does some prototype checking to make `isA<JSTypedArray>`
|
/// JavaScript, this does some prototype checking to make `isA<JSTypedArray>`
|
||||||
/// do the right thing. The other exception is `JSAny`. If you do a
|
/// 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
|
/// 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.
|
/// result in an error as you likely want to use [JSObject] instead.
|
||||||
|
@ -517,25 +519,27 @@ extension ObjectToJSBoxedDartObject on Object {
|
||||||
external JSBoxedDartObject get toJSBox;
|
external JSBoxedDartObject get toJSBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conversions from [ExternalDartReference] to [Object].
|
/// Conversions from [ExternalDartReference] to the value of type [T].
|
||||||
extension ExternalDartReferenceToObject on ExternalDartReference {
|
extension ExternalDartReferenceToObject<T extends Object?>
|
||||||
/// The Dart [Object] that this [ExternalDartReference] refers to.
|
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
|
/// When compiling to JavaScript, a Dart object is a JavaScript object, and
|
||||||
/// therefore this directly returns the Dart object. When compiling to Wasm,
|
/// therefore this directly returns the Dart object. When compiling to Wasm,
|
||||||
/// an internal Wasm function is used to convert the opaque JavaScript value
|
/// an internal Wasm function is used to convert the opaque JavaScript value
|
||||||
/// to the original Dart object.
|
/// to the original Dart object.
|
||||||
external Object get toDartObject;
|
external T get toDartObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conversions from [Object] to [ExternalDartReference].
|
/// Conversions from a value of type [T] to [ExternalDartReference].
|
||||||
extension ObjectToExternalDartReference on Object {
|
extension ObjectToExternalDartReference<T extends Object?> on T {
|
||||||
/// An opaque reference to this [Object] which can be passed to JavaScript.
|
/// 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
|
/// When compiling to JavaScript, a Dart object is a JavaScript object, and
|
||||||
/// therefore this directly returns the Dart object. When compiling to Wasm,
|
/// 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
|
/// 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
|
/// A value of type [ExternalDartReference] should be treated as completely
|
||||||
/// opaque. It can only be passed around as-is or converted back using
|
/// 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
|
/// guaranteed to be equal. Therefore, `==` will always return true between
|
||||||
/// such [ExternalDartReference]s. However, like JS types, `identical` between
|
/// such [ExternalDartReference]s. However, like JS types, `identical` between
|
||||||
/// such values may return different results depending on the compiler.
|
/// such values may return different results depending on the compiler.
|
||||||
external ExternalDartReference get toExternalReference;
|
external ExternalDartReference<T> get toExternalReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conversions from [JSPromise] to [Future].
|
/// 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.
|
// [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();
|
external EExternalDartReference();
|
||||||
// ^
|
// ^
|
||||||
// [web] Extension type member is marked 'external', but the representation type of its extension type is not a valid JS interop type.
|
// [web] Extension type member is marked 'external', but the representation type of its extension type is not a valid JS interop type.
|
||||||
}
|
}
|
||||||
|
|
||||||
@JS()
|
@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 {
|
extension on EExternalDartReference {
|
||||||
external int field;
|
external int field;
|
||||||
|
|
|
@ -7,30 +7,34 @@
|
||||||
import 'dart:js_interop';
|
import 'dart:js_interop';
|
||||||
|
|
||||||
import 'package:expect/expect.dart';
|
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 _)
|
extension type EExternalDartReference<T>(ExternalDartReference<T> _)
|
||||||
implements ExternalDartReference {}
|
implements ExternalDartReference<T> {}
|
||||||
|
|
||||||
@JS()
|
@JS()
|
||||||
external ExternalDartReference externalDartReference;
|
external ExternalDartReference<Object> externalDartReference;
|
||||||
|
|
||||||
@JS()
|
@JS()
|
||||||
external EExternalDartReference eExternalDartReference;
|
external EExternalDartReference<DartClass> eExternalDartReference;
|
||||||
|
|
||||||
@JS('externalDartReference')
|
@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
|
// Use a function so that we can use a type parameter that extends an
|
||||||
// `ExternalDartReference` type.
|
// `ExternalDartReference` type.
|
||||||
@JS('identity')
|
@JS('identity')
|
||||||
external set _identity(JSFunction _);
|
external set _identity(JSFunction _);
|
||||||
@JS()
|
@JS()
|
||||||
external T identity<T extends EExternalDartReference>(T t);
|
external T identity<T extends ExternalDartReference?>(T t);
|
||||||
|
|
||||||
extension type ObjectLiteral(JSObject _) {
|
extension type ObjectLiteral(JSObject _) {
|
||||||
external void operator []=(String key, ExternalDartReference value);
|
external void operator []=(String key, ExternalDartReference<Object> value);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DartClass {
|
class DartClass {
|
||||||
|
@ -39,23 +43,37 @@ class DartClass {
|
||||||
DartClass(this.field);
|
DartClass(this.field);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
class DartSubclass extends DartClass {
|
||||||
final dartObject = DartClass(42);
|
DartSubclass(super.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generalTest() {
|
||||||
|
var dartObject = DartClass(42);
|
||||||
|
|
||||||
|
// `Object` test.
|
||||||
externalDartReference = dartObject.toExternalReference;
|
externalDartReference = dartObject.toExternalReference;
|
||||||
Expect.equals(dartObject, externalDartReference.toDartObject as DartClass);
|
Expect.equals(dartObject, externalDartReference.toDartObject as DartClass);
|
||||||
Expect.isTrue(
|
Expect.identical(dartObject, externalDartReference.toDartObject as DartClass);
|
||||||
identical(dartObject, externalDartReference.toDartObject as DartClass));
|
|
||||||
eExternalDartReference = EExternalDartReference(externalDartReference);
|
// Generic test.
|
||||||
Expect.equals(dartObject, eExternalDartReference.toDartObject as DartClass);
|
var externalDartClassReference = dartObject.toExternalReference;
|
||||||
Expect.isTrue(
|
Expect.equals(dartObject, externalDartClassReference.toDartObject);
|
||||||
identical(dartObject, eExternalDartReference.toDartObject as DartClass));
|
Expect.identical(dartObject, externalDartClassReference.toDartObject);
|
||||||
_identity = ((ExternalDartReference e) => e).toJS;
|
// 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);
|
final externalDartReferenceTypeParam = identity(eExternalDartReference);
|
||||||
Expect.equals(
|
Expect.equals(dartObject, externalDartReferenceTypeParam.toDartObject);
|
||||||
dartObject, externalDartReferenceTypeParam.toDartObject as DartClass);
|
Expect.identical(dartObject, externalDartReferenceTypeParam.toDartObject);
|
||||||
Expect.isTrue(identical(
|
|
||||||
dartObject, externalDartReferenceTypeParam.toDartObject as DartClass));
|
|
||||||
|
|
||||||
// Multiple invocations should return the same underlying value, which is
|
// Multiple invocations should return the same underlying value, which is
|
||||||
// tested by `==`.
|
// tested by `==`.
|
||||||
|
@ -63,27 +81,111 @@ void main() {
|
||||||
// However, they may or may not be identical depending on the compiler due to
|
// However, they may or may not be identical depending on the compiler due to
|
||||||
// dart2wasm wrapping values with new JSValue instances.
|
// dart2wasm wrapping values with new JSValue instances.
|
||||||
if (isJSBackend) {
|
if (isJSBackend) {
|
||||||
Expect.isTrue(
|
Expect.identical(externalDartReference, dartObject.toExternalReference);
|
||||||
identical(externalDartReference, dartObject.toExternalReference));
|
|
||||||
} else {
|
} else {
|
||||||
Expect.isFalse(
|
Expect.isFalse(
|
||||||
identical(externalDartReference, dartObject.toExternalReference));
|
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
|
// 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
|
// be expensive to validate. We end up externalizing the JSValue wrapper in
|
||||||
// this case.
|
// this case.
|
||||||
|
final jsString = ''.toJS;
|
||||||
Expect.equals(jsString.toExternalReference.toDartObject, jsString);
|
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.
|
// Check that the type is checked when internalized for soundness.
|
||||||
nullableExternalDartReference = null;
|
externalDartReference = dartObject.toExternalReference;
|
||||||
if (hasSoundNullSafety) Expect.throws(() => externalDartReference);
|
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`.
|
// Functions should not trigger `assertInterop`.
|
||||||
externalDartReference = () {}.toExternalReference;
|
externalDartReference = () {}.toExternalReference;
|
||||||
identity(EExternalDartReference(() {}.toExternalReference));
|
identity(EExternalDartReference(() {}.toExternalReference));
|
||||||
final literal = ObjectLiteral(JSObject());
|
ObjectLiteral(JSObject())['ref'] = externalDartReference;
|
||||||
literal['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