[dart:js_interop/ddc/dart2js] Implement JS types using @staticInterop

Currently, dart:_js_types types are all typedefs in the web backends.
This leads to inconsistent semantics, since you can statically pass
Strings to JSString, for example. You cannot do this in dart2wasm.
In order to ensure consistent semantics, we reify these types using
a custom @staticInterop lowering. They all get erased to their
respective Dart type. When we have inline classes, these types
should be implemented using inline classes.

Note that Interceptor will not work for this use case. The reified
type of JS primitives are Dart types e.g. String, bool, and therefore
can not be casted to Interceptor.

In order to do this, the eraser is refactored and the JS backends use
shared erasure code to either erase/emit types.

Tests are added to make sure you need to go through a conversion or
cast to pass Dart objects to JS types.

CoreLibraryReviewExempt: Backend-specific internal library changes.
Change-Id: I5942be628102919ec167f094cfe10fced606363c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/295105
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
Srujan Gaddam 2023-04-26 15:55:54 +00:00 committed by Commit Queue
parent e506581534
commit 1d28f8e821
12 changed files with 483 additions and 92 deletions

View file

@ -13,14 +13,97 @@ import 'package:kernel/reference_from_index.dart';
import 'package:kernel/src/constant_replacer.dart';
import 'package:kernel/src/replacement_visitor.dart';
/// Erasure function for `dart:_js_types` `@staticInterop` types.
InterfaceType transformJSTypesForJSCompilers(
CoreTypes coreTypes, InterfaceType staticInteropType) {
final className = staticInteropType.classNode.name;
Class erasedClass;
var typeArguments = staticInteropType.typeArguments;
switch (className) {
case 'JSAny':
erasedClass = coreTypes.objectClass;
break;
case 'JSObject':
erasedClass = coreTypes.index.getClass('dart:_interceptors', 'JSObject');
break;
case 'JSFunction':
erasedClass = coreTypes.functionClass;
break;
case 'JSExportedDartFunction':
erasedClass = coreTypes.functionClass;
break;
case 'JSArray':
erasedClass = coreTypes.listClass;
typeArguments = [coreTypes.objectNullableRawType];
break;
case 'JSExportedDartObject':
erasedClass = coreTypes.objectClass;
break;
case 'JSArrayBuffer':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'ByteBuffer');
break;
case 'JSDataView':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'ByteData');
break;
case 'JSTypedArray':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'TypedData');
break;
case 'JSInt8Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Int8List');
break;
case 'JSUint8Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Uint8List');
break;
case 'JSUint8ClampedArray':
erasedClass =
coreTypes.index.getClass('dart:typed_data', 'Uint8ClampedList');
break;
case 'JSInt16Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Int16List');
break;
case 'JSUint16Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Uint16List');
break;
case 'JSInt32Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Int32List');
break;
case 'JSUint32Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Uint32List');
break;
case 'JSFloat32Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Float32List');
break;
case 'JSFloat64Array':
erasedClass = coreTypes.index.getClass('dart:typed_data', 'Float64List');
break;
case 'JSNumber':
erasedClass = coreTypes.doubleClass;
break;
case 'JSBoolean':
erasedClass = coreTypes.boolClass;
break;
case 'JSString':
erasedClass = coreTypes.stringClass;
break;
case 'JSPromise':
erasedClass = coreTypes.index.getClass('dart:_interceptors', 'JSObject');
break;
default:
throw 'Unimplemented `dart:_js_types`: $className';
}
return InterfaceType(
erasedClass, staticInteropType.declaredNullability, typeArguments);
}
class _TypeSubstitutor extends ReplacementVisitor {
final Class _javaScriptObject;
_TypeSubstitutor(this._javaScriptObject);
final InterfaceType Function(InterfaceType staticInteropType)
_eraseStaticInteropType;
_TypeSubstitutor(this._eraseStaticInteropType);
@override
DartType? visitInterfaceType(InterfaceType type, int variance) {
if (hasStaticInteropAnnotation(type.classNode)) {
return InterfaceType(_javaScriptObject, type.declaredNullability);
return _eraseStaticInteropType(type);
}
return super.visitInterfaceType(type, variance);
}
@ -29,12 +112,16 @@ class _TypeSubstitutor extends ReplacementVisitor {
/// Erases usage of `@JS` classes that are annotated with `@staticInterop` in
/// favor of `JavaScriptObject`.
class StaticInteropClassEraser extends Transformer {
final Class _javaScriptObject;
final CloneVisitorNotMembers _cloner = CloneVisitorNotMembers();
late final _StaticInteropConstantReplacer _constantReplacer;
late final _TypeSubstitutor _typeSubstitutor;
Component? currentComponent;
ReferenceFromIndex? referenceFromIndex;
// Custom erasure function for `@staticInterop` types. This is useful for when
// they should be erased to another type besides `JavaScriptObject`, like in
// dart2wasm.
late final InterfaceType Function(InterfaceType staticInteropType)
_eraseStaticInteropType;
// Visiting core libraries that don't contain `@staticInterop` adds overhead.
// To avoid this, we use an allowlist that contains libraries that we know use
// `@staticInterop`.
@ -45,12 +132,22 @@ class StaticInteropClassEraser extends Transformer {
};
StaticInteropClassEraser(CoreTypes coreTypes, this.referenceFromIndex,
{String libraryForJavaScriptObject = 'dart:_interceptors',
String classNameOfJavaScriptObject = 'JavaScriptObject',
Set<String> additionalCoreLibraries = const {}})
: _javaScriptObject = coreTypes.index
.getClass(libraryForJavaScriptObject, classNameOfJavaScriptObject) {
_typeSubstitutor = _TypeSubstitutor(_javaScriptObject);
{InterfaceType Function(InterfaceType staticInteropType)?
eraseStaticInteropType,
Set<String> additionalCoreLibraries = const {}}) {
final dartJsTypes = Uri.parse('dart:_js_types');
_eraseStaticInteropType = eraseStaticInteropType ??
(staticInteropType) {
if (staticInteropType.classNode.enclosingLibrary.importUri ==
dartJsTypes) {
return transformJSTypesForJSCompilers(coreTypes, staticInteropType);
}
return InterfaceType(
coreTypes.index
.getClass('dart:_interceptors', 'JavaScriptObject'),
staticInteropType.declaredNullability);
};
_typeSubstitutor = _TypeSubstitutor(_eraseStaticInteropType);
_constantReplacer = _StaticInteropConstantReplacer(this);
_erasableCoreLibraries.addAll(additionalCoreLibraries);
}
@ -178,8 +275,8 @@ class StaticInteropClassEraser extends Transformer {
var newInvocation = super.visitConstructorInvocation(node) as Expression;
return AsExpression(
newInvocation,
InterfaceType(_javaScriptObject,
node.target.function.returnType.declaredNullability))
_eraseStaticInteropType(
node.target.function.returnType as InterfaceType))
..fileOffset = newInvocation.fileOffset;
}
return super.visitConstructorInvocation(node);
@ -208,9 +305,7 @@ class StaticInteropClassEraser extends Transformer {
// Add a cast so that the result gets typed as `JavaScriptObject`.
var newInvocation = super.visitStaticInvocation(node) as Expression;
return AsExpression(
newInvocation,
InterfaceType(_javaScriptObject,
node.target.function.returnType.declaredNullability))
newInvocation, node.target.function.returnType as InterfaceType)
..fileOffset = newInvocation.fileOffset;
}
}

View file

@ -112,7 +112,8 @@ class Dart2jsTarget extends Target {
'dart:_late_helper',
'dart:js',
'dart:js_interop',
'dart:js_util'
'dart:js_util',
'dart:typed_data',
];
@override

View file

@ -133,7 +133,9 @@ void _doTransformsOnKernelLoad(Component? component) {
// containing a stub definition is invalidated, and then reloaded, because
// we need to keep existing references to that stub valid. Here, we have the
// whole program, and therefore do not need it.
StaticInteropClassEraser(ir.CoreTypes(component), null)
ir.CoreTypes coreTypes = ir.CoreTypes(component);
StaticInteropClassEraser(coreTypes, null,
additionalCoreLibraries: {'_js_types', 'js_interop'})
.visitComponent(component);
}

View file

@ -25,9 +25,10 @@ JSMethods _performJSInteropTransformations(
// We want static types to help us specialize methods based on receivers.
// Therefore, erasure must come after the lowering.
final jsValueClass = coreTypes.index.getClass('dart:_js_helper', 'JSValue');
final staticInteropClassEraser = StaticInteropClassEraser(coreTypes, null,
libraryForJavaScriptObject: 'dart:_js_helper',
classNameOfJavaScriptObject: 'JSValue',
eraseStaticInteropType: (staticInteropType) =>
InterfaceType(jsValueClass, staticInteropType.declaredNullability),
additionalCoreLibraries: {'_js_helper', '_js_types', 'js_interop'});
for (Library library in interopDependentLibraries) {
staticInteropClassEraser.visitLibrary(library);

View file

@ -7,6 +7,8 @@ import 'dart:convert';
import 'dart:io' as io;
import 'dart:math' show max, min;
import 'package:_js_interop_checks/src/transformations/static_interop_class_eraser.dart'
show transformJSTypesForJSCompilers;
import 'package:collection/collection.dart'
show IterableExtension, IterableNullableExtension;
import 'package:front_end/src/api_unstable/ddc.dart';
@ -3231,8 +3233,13 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
? getLocalClassName(c)
: _emitJsNameWithoutGlobal(c);
if (jsName != null) {
typeRep = runtimeCall('packageJSType(#, #)',
[js.escapedString(jsName), js.boolean(isStaticInteropType(c))]);
if (isDartJSTypesType(c)) {
typeRep = visitInterfaceType(
transformJSTypesForJSCompilers(_coreTypes, type));
} else {
typeRep = runtimeCall('packageJSType(#, #)',
[js.escapedString(jsName), js.boolean(isStaticInteropType(c))]);
}
}
if (typeRep != null) {

View file

@ -138,6 +138,12 @@ bool hasObjectLiteralAnnotation(Annotatable a) =>
// need to be `external` in an `@JS` class.
bool hasJSInteropAnnotation(Class c) => c.annotations.any(isPublicJSAnnotation);
/// Returns true iff [c] is a class from `dart:_js_types` implemented using
/// `@staticInterop`.
bool isDartJSTypesType(Class c) =>
_isLibrary(c.enclosingLibrary, const ['dart:_js_types']) &&
isStaticInteropType(c);
/// Returns true iff this element is a JS interop member.
///
/// JS annotations are required explicitly on classes. Other elements, such as

View file

@ -102,6 +102,7 @@ class DevCompilerTarget extends Target {
'dart:js_interop',
'dart:math',
'dart:svg',
'dart:typed_data',
'dart:web_audio',
'dart:web_gl',
'dart:_foreign_helper',

View file

@ -21,12 +21,13 @@ extension NullableUndefineableJSAnyExtension on JSAny? {
/// [JSExportedDartFunction] <-> [Function]
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
@patch
Function get toDart => this;
Function get toDart => this as Function;
}
extension FunctionToJSExportedDartFunction on Function {
@patch
JSExportedDartFunction get toJS => allowInterop(this);
JSExportedDartFunction get toJS =>
allowInterop(this) as JSExportedDartFunction;
}
/// [JSExportedDartObject] <-> [Object]
@ -37,7 +38,7 @@ extension JSExportedDartObjectToObject on JSExportedDartObject {
extension ObjectToJSExportedDartObject on Object {
@patch
JSExportedDartObject get toJS => this;
JSExportedDartObject get toJS => this as JSExportedDartObject;
}
/// [JSPromise] -> [Future<JSAny?>].
@ -49,122 +50,122 @@ extension JSPromiseToFuture on JSPromise {
/// [JSArrayBuffer] <-> [ByteBuffer]
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
@patch
ByteBuffer get toDart => this;
ByteBuffer get toDart => this as ByteBuffer;
}
extension ByteBufferToJSArrayBuffer on ByteBuffer {
@patch
JSArrayBuffer get toJS => this;
JSArrayBuffer get toJS => this as JSArrayBuffer;
}
/// [JSDataView] <-> [ByteData]
extension JSDataViewToByteData on JSDataView {
@patch
ByteData get toDart => this;
ByteData get toDart => this as ByteData;
}
extension ByteDataToJSDataView on ByteData {
@patch
JSDataView get toJS => this;
JSDataView get toJS => this as JSDataView;
}
/// [JSInt8Array] <-> [Int8List]
extension JSInt8ArrayToInt8List on JSInt8Array {
@patch
Int8List get toDart => this;
Int8List get toDart => this as Int8List;
}
extension Int8ListToJSInt8Array on Int8List {
@patch
JSInt8Array get toJS => this;
JSInt8Array get toJS => this as JSInt8Array;
}
/// [JSUint8Array] <-> [Uint8List]
extension JSUint8ArrayToUint8List on JSUint8Array {
@patch
Uint8List get toDart => this;
Uint8List get toDart => this as Uint8List;
}
extension Uint8ListToJSUint8Array on Uint8List {
@patch
JSUint8Array get toJS => this;
JSUint8Array get toJS => this as JSUint8Array;
}
/// [JSUint8ClampedArray] <-> [Uint8ClampedList]
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
@patch
Uint8ClampedList get toDart => this;
Uint8ClampedList get toDart => this as Uint8ClampedList;
}
extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList {
@patch
JSUint8ClampedArray get toJS => this;
JSUint8ClampedArray get toJS => this as JSUint8ClampedArray;
}
/// [JSInt16Array] <-> [Int16List]
extension JSInt16ArrayToInt16List on JSInt16Array {
@patch
Int16List get toDart => this;
Int16List get toDart => this as Int16List;
}
extension Int16ListToJSInt16Array on Int16List {
@patch
JSInt16Array get toJS => this;
JSInt16Array get toJS => this as JSInt16Array;
}
/// [JSUint16Array] <-> [Uint16List]
extension JSUint16ArrayToInt16List on JSUint16Array {
@patch
Uint16List get toDart => this;
Uint16List get toDart => this as Uint16List;
}
extension Uint16ListToJSInt16Array on Uint16List {
@patch
JSUint16Array get toJS => this;
JSUint16Array get toJS => this as JSUint16Array;
}
/// [JSInt32Array] <-> [Int32List]
extension JSInt32ArrayToInt32List on JSInt32Array {
@patch
Int32List get toDart => this;
Int32List get toDart => this as Int32List;
}
extension Int32ListToJSInt32Array on Int32List {
@patch
JSInt32Array get toJS => this;
JSInt32Array get toJS => this as JSInt32Array;
}
/// [JSUint32Array] <-> [Uint32List]
extension JSUint32ArrayToUint32List on JSUint32Array {
@patch
Uint32List get toDart => this;
Uint32List get toDart => this as Uint32List;
}
extension Uint32ListToJSUint32Array on Uint32List {
@patch
JSUint32Array get toJS => this;
JSUint32Array get toJS => this as JSUint32Array;
}
/// [JSFloat32Array] <-> [Float32List]
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
@patch
Float32List get toDart => this;
Float32List get toDart => this as Float32List;
}
extension Float32ListToJSFloat32Array on Float32List {
@patch
JSFloat32Array get toJS => this;
JSFloat32Array get toJS => this as JSFloat32Array;
}
/// [JSFloat64Array] <-> [Float64List]
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
@patch
Float64List get toDart => this;
Float64List get toDart => this as Float64List;
}
extension Float64ListToJSFloat64Array on Float64List {
@patch
JSFloat64Array get toJS => this;
JSFloat64Array get toJS => this as JSFloat64Array;
}
/// [JSArray] <-> [List]
@ -175,38 +176,38 @@ extension JSArrayToList on JSArray {
extension ListToJSArray on List<JSAny?> {
@patch
JSArray get toJS => this;
JSArray get toJS => this as JSArray;
}
/// [JSNumber] <-> [double]
extension JSNumberToDouble on JSNumber {
@patch
double get toDart => this;
double get toDart => this as double;
}
extension DoubleToJSNumber on double {
@patch
JSNumber get toJS => this;
JSNumber get toJS => this as JSNumber;
}
/// [JSBoolean] <-> [bool]
extension JSBooleanToBool on JSBoolean {
@patch
bool get toDart => this;
bool get toDart => this as bool;
}
extension BoolToJSBoolean on bool {
@patch
JSBoolean get toJS => this;
JSBoolean get toJS => this as JSBoolean;
}
/// [JSString] <-> [String]
extension JSStringToString on JSString {
@patch
String get toDart => this;
String get toDart => this as String;
}
extension StringToJSString on String {
@patch
JSString get toJS => this;
JSString get toJS => this as JSString;
}

View file

@ -4,47 +4,126 @@
/// This library exists to act as a uniform abstraction layer between the user
/// facing JS interop libraries and backend specific internal representations of
/// JS types. For consistency, all of the web backends have a version of this
/// library.
/// JS types.
///
/// For consistency, all of the web backends have a version of this library.
///
/// For the time being, all JS types are erased to their respective Dart type at
/// runtime e.g. [JSString] -> [String]. Eventually, when we have inline
/// classes, we may choose to either:
///
/// 1. Use [Object] as the representation type.
/// 2. Have some analog to dart2wasm's [JSValue] as the representation type in
/// order to separate the Dart and JS type hierarchies at runtime.
/// 3. Continue using the respective Dart type.
///
/// Note that we can't use [Interceptor] to do option #2. [Interceptor] is a
/// supertype of types like [interceptors.JSString], but not a supertype of the
/// core types like [String]. This becomes relevant when we use external APIs.
/// External APIs get lowered to `js_util` calls, which cast the return value.
/// If a function returns a JavaScript string, it gets reified as a Dart
/// [String] for example. Then when we cast to [JSString] in `js_util`, we get
/// a cast failure, as [String] is not a subtype of [Interceptor].
///
/// For specific details of the JS type hierarchy, please see
/// `sdk/lib/js_interop/js_interop.dart`.
library _js_types;
import 'dart:_js_annotations';
import 'dart:_interceptors' as interceptors;
import 'dart:_internal' show patch;
import 'dart:typed_data';
/// For the time being, all JS types are conflated with Dart types on JS
/// backends. See `sdk/lib/_internal/wasm/lib/js_types.dart` for more details.
@JS()
@staticInterop
class JSAny {}
/// For specific details of the JS type hierarchy, please see
/// `sdk/lib/js_interop/js_interop.dart`.
/// TODO(joshualitt): Some users may want type safety instead of conflating Dart
/// types and JS types. For those users, we should have an opt-in flag where we
/// swap out these typedef for actual types that would not implicitly coerce
/// statically to their Dart counterparts and back.
typedef JSAny = Object;
typedef JSObject = interceptors.JSObject;
typedef JSFunction = Function;
typedef JSExportedDartFunction = Function;
typedef JSArray = List<JSAny?>;
typedef JSExportedDartObject = Object;
typedef JSArrayBuffer = ByteBuffer;
typedef JSDataView = ByteData;
typedef JSTypedArray = TypedData;
typedef JSInt8Array = Int8List;
typedef JSUint8Array = Uint8List;
typedef JSUint8ClampedArray = Uint8ClampedList;
typedef JSInt16Array = Int16List;
typedef JSUint16Array = Uint16List;
typedef JSInt32Array = Int32List;
typedef JSUint32Array = Uint32List;
typedef JSFloat32Array = Float32List;
typedef JSFloat64Array = Float64List;
typedef JSNumber = double;
typedef JSBoolean = bool;
typedef JSString = String;
@JS()
@staticInterop
class JSObject implements JSAny {}
@JS()
@staticInterop
class JSFunction implements JSObject {}
@JS()
@staticInterop
class JSExportedDartFunction implements JSFunction {}
@JS('Array')
@staticInterop
class JSArray implements JSObject {
external factory JSArray();
external factory JSArray.withLength(JSNumber length);
}
@JS()
@staticInterop
class JSExportedDartObject implements JSObject {}
@JS()
@staticInterop
class JSArrayBuffer implements JSObject {}
@JS()
@staticInterop
class JSDataView implements JSObject {}
@JS()
@staticInterop
class JSTypedArray implements JSObject {}
@JS()
@staticInterop
class JSInt8Array implements JSTypedArray {}
@JS()
@staticInterop
class JSUint8Array implements JSTypedArray {}
@JS()
@staticInterop
class JSUint8ClampedArray implements JSTypedArray {}
@JS()
@staticInterop
class JSInt16Array implements JSTypedArray {}
@JS()
@staticInterop
class JSUint16Array implements JSTypedArray {}
@JS()
@staticInterop
class JSInt32Array implements JSTypedArray {}
@JS()
@staticInterop
class JSUint32Array implements JSTypedArray {}
@JS()
@staticInterop
class JSFloat32Array implements JSTypedArray {}
@JS()
@staticInterop
class JSFloat64Array implements JSTypedArray {}
@JS()
@staticInterop
class JSNumber implements JSAny {}
@JS()
@staticInterop
class JSBoolean implements JSAny {}
@JS()
@staticInterop
class JSString implements JSAny {}
/// [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.
typedef JSVoid = void;
@JS()
@staticInterop
class JSPromise {}
class JSPromise implements JSObject {}

View file

@ -11,11 +11,13 @@ library _js_types;
import 'dart:_js_annotations';
/// Note that the semantics of JS types on Wasm backends are slightly different
/// from the JS backends as we use static interop, and thus [JSValue], to
/// implement all of the other JS types, whereas the JS backends conflate Dart
/// types and JS types. Because we're not sure exactly where things will end
/// up, we're moving gradually towards consistent semantics across all web
/// backends. A gradual path to consistent semantics might look something like:
/// from the JS backends. They all use `@staticInterop` currently, but Wasm
/// erases to [JSValue], while the JS backends erase each JS type to its
/// respective Dart type.
///
/// Because we're not sure exactly where things will end up, we're moving
/// gradually towards consistent semantics across all web backends. A gradual
/// path to consistent semantics might look something like:
/// 1) Launch MVP with JS backends conflating Dart types and JS types, and Wasm
/// backends implementing JS types with boxes. On Wasm backends, users will
/// have to explicitly coerce Dart types to JS types, possibly with some

View file

@ -43,6 +43,19 @@ class ObjectLiteral {
const ObjectLiteral();
}
/// The JS types users should use to write their external APIs.
///
/// These are meant to separate the Dart and JS type hierarchies statically.
///
/// **WARNING**:
/// For now, the runtime semantics between backends may differ and may not be
/// intuitive e.g. casting to [JSString] may give you inconsistent and
/// surprising results depending on the value. It is preferred to always use the
/// conversion functions e.g. `toJS` and `toDart`. The only runtime semantics
/// stability we can guarantee is if a value actually is the JS type you are
/// type-checking with/casting to e.g. `obj as JSString` will continue to work
/// if `obj` actually is a JavaScript string.
/// The overall top type in the JS types hierarchy.
typedef JSAny = js_types.JSAny;

View file

@ -0,0 +1,183 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Check that JS types are not typedefs and are actual separate types. This
// makes sure that users can't assign unrelated types to the JS type without a
// conversion.
import 'dart:js_interop';
import 'dart:typed_data';
class DartObject {}
void main() {
// [JSAny] != [Object]
((JSAny jsAny) {})(DartObject() as Object);
// ^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// ^
// [web] The argument type 'Object' can't be assigned to the parameter type 'JSAny'.
// [JSObject] != [Object]
((JSObject jsObj) {})(DartObject() as Object);
// ^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// ^
// [web] The argument type 'Object' can't be assigned to the parameter type 'JSObject'.
// [JSFunction] != [Function]
((JSFunction jsFun) {})(() {} as Function);
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// ^
// [web] The argument type 'Function' can't be assigned to the parameter type 'JSFunction'.
// [JSExportedDartFunction] != [Function]
((JSExportedDartFunction jsFun) {})(() {} as Function);
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// ^
// [web] The argument type 'Function' can't be assigned to the parameter type 'JSExportedDartFunction'.
// [JSExportedDartObject] != [Object]
((JSExportedDartObject jsObj) {})(DartObject());
// ^
// [web] The argument type 'DartObject' can't be assigned to the parameter type 'JSExportedDartObject'.
// ^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSArray] != [List<JSAny?>]
List<JSAny?> dartArr = <JSAny?>[1.0.toJS, 'foo'.toJS];
((JSArray jsArr) {})(dartArr);
// ^
// [web] The argument type 'List<JSAny?>' can't be assigned to the parameter type 'JSArray'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSArrayBuffer] != [ByteBuffer]
ByteBuffer dartBuf = Uint8List.fromList([0, 255, 0, 255]).buffer;
((JSArrayBuffer jsBuf) {})(dartBuf);
// ^
// [web] The argument type 'ByteBuffer' can't be assigned to the parameter type 'JSArrayBuffer'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSDataView] != [ByteData]
ByteData dartDat = Uint8List.fromList([0, 255, 0, 255]).buffer.asByteData();
((JSDataView jsDat) {})(dartDat);
// ^
// [web] The argument type 'ByteData' can't be assigned to the parameter type 'JSDataView'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSTypedArray]s != [TypedData]s
TypedData typedData = Int8List.fromList([-128, 0, 127]);
((JSTypedArray jsTypedArray) {})(typedData);
// ^
// [web] The argument type 'TypedData' can't be assigned to the parameter type 'JSTypedArray'.
// ^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSInt8Array]s != [Int8List]s
Int8List ai8 = Int8List.fromList([-128, 0, 127]);
((JSInt8Array jsAi8) {})(ai8);
// ^
// [web] The argument type 'Int8List' can't be assigned to the parameter type 'JSInt8Array'.
// ^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSUint8Array] != [Uint8List]
Uint8List au8 = Uint8List.fromList([-1, 0, 255, 256]);
((JSUint8Array jsAu8) {})(au8);
// ^
// [web] The argument type 'Uint8List' can't be assigned to the parameter type 'JSUint8Array'.
// ^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSUint8ClampedArray] != [Uint8ClampedList]
Uint8ClampedList ac8 = Uint8ClampedList.fromList([-1, 0, 255, 256]);
((JSUint8ClampedArray jsAc8) {})(ac8);
// ^
// [web] The argument type 'Uint8ClampedList' can't be assigned to the parameter type 'JSUint8ClampedArray'.
// ^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSInt16Array] != [Int16List]
Int16List ai16 = Int16List.fromList([-32769, -32768, 0, 32767, 32768]);
((JSInt16Array jsAi16) {})(ai16);
// ^
// [web] The argument type 'Int16List' can't be assigned to the parameter type 'JSInt16Array'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSUint16Array] != [Uint16List]
Uint16List au16 = Uint16List.fromList([-1, 0, 65535, 65536]);
((JSUint16Array jsAu16) {})(au16);
// ^
// [web] The argument type 'Uint16List' can't be assigned to the parameter type 'JSUint16Array'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSInt32Array] != [Int32List]
Int32List ai32 = Int32List.fromList([-2147483648, 0, 2147483647]);
((JSInt32Array jsAi32) {})(ai32);
// ^
// [web] The argument type 'Int32List' can't be assigned to the parameter type 'JSInt32Array'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSUint32Array] != [Uint32List]
Uint32List au32 = Uint32List.fromList([-1, 0, 4294967295, 4294967296]);
((JSUint32Array jsAu32) {})(au32);
// ^
// [web] The argument type 'Uint32List' can't be assigned to the parameter type 'JSUint32Array'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSFloat32Array] != [Float32List]
Float32List af32 =
Float32List.fromList([-1000.488, -0.00001, 0.0001, 10004.888]);
((JSFloat32Array jsAf32) {})(af32);
// ^
// [web] The argument type 'Float32List' can't be assigned to the parameter type 'JSFloat32Array'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSFloat64Array] != [Float64List]
Float64List af64 =
Float64List.fromList([-1000.488, -0.00001, 0.0001, 10004.888]);
((JSFloat64Array jsAf64) {})(af64);
// ^
// [web] The argument type 'Float64List' can't be assigned to the parameter type 'JSFloat64Array'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSNumber] != [double]
((JSNumber jsNum) {})(4.5);
// ^
// [web] The argument type 'double' can't be assigned to the parameter type 'JSNumber'.
// ^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSBoolean] != [bool]
((JSBoolean jsBool) {})(true);
// ^
// [web] The argument type 'bool' can't be assigned to the parameter type 'JSBoolean'.
// ^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSString] != [String]
((JSString jsStr) {})('foo');
// ^
// [web] The argument type 'String' can't be assigned to the parameter type 'JSString'.
// ^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
// [JSPromise] != [Future]
((JSPromise promise) {})(Future<void>.delayed(Duration.zero));
// ^
// [web] The argument type 'Future<void>' can't be assigned to the parameter type 'JSPromise'.
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
}