diff --git a/LibTest/js_interop/anonymous_A03_t04.dart b/LibTest/js_interop/anonymous_A03_t04.dart index 55ced9f92f..67bda3535e 100644 --- a/LibTest/js_interop/anonymous_A03_t04.dart +++ b/LibTest/js_interop/anonymous_A03_t04.dart @@ -13,6 +13,7 @@ /// @description Checks that it is a compile-time error if an extension /// is annotated with `@anonymous`. /// @author sgrekhov22@gmail.com +/// @issue 61082 import 'dart:js_interop'; diff --git a/LibTest/js_interop/staticInterop_A03_t05.dart b/LibTest/js_interop/staticInterop_A03_t05.dart new file mode 100644 index 0000000000..26bf8b8f42 --- /dev/null +++ b/LibTest/js_interop/staticInterop_A03_t05.dart @@ -0,0 +1,132 @@ +// Copyright (c) 2025, 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. + +/// @assertion `staticInterop` enables the JS annotated class to be treated as +/// a "static" interop class. +/// +/// These classes implicitly all erase to the internal interceptor +/// `JavaScriptObject`, so they can be freely casted to and from other +/// [staticInterop] types, `dart:html` types, and `JSObject` from +/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to +/// [staticInterop] types, but the reverse can fail if the underlying value is a +/// `@Native`-reserved type (like `dart:html` types). +/// +/// [staticInterop] classes have the following restrictions: +/// - They must contain a [JS] annotation, either from this library or from +/// `dart:js_interop`. +/// - They should not contain any instance members, inherited or otherwise, and +/// should instead use static extension members, which can be external or +/// non-external. +/// - They can only contain factories and `static` members. They can be +/// combined with [anonymous] to make external factories create new +/// JavaScript object literals instead. +/// - They should not implement, extend, or mixin non-[staticInterop] classes +/// and vice-versa. +/// - The annotation should only be applied to non-mixin classes and no other +/// declarations. +/// +/// @description Checks that a class annotated with `@staticInterop` can be +/// `abstract`, `base`, `final`, `sealed` or `interface`. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import '../../Utils/expect.dart'; +import 'js_utils.dart'; + +@staticInterop +@JS() +abstract class C1 { +} + +@staticInterop +@JS() +base class C2 { +} + +@staticInterop +@JS() +final class C3 { +} + +@staticInterop +@JS() +sealed class C4 { +} + +@staticInterop +@JS() +interface class C5 { +} + +extension Ext1 on C1 { + external String getString(); + external int getNumber(); + external bool getBool(); +} + +extension Ext2 on C2 { + external String getString(); + external int getNumber(); + external bool getBool(); +} + +extension Ext3 on C3 { + external String getString(); + external int getNumber(); + external bool getBool(); +} + +extension Ext4 on C4 { + external String getString(); + external int getNumber(); + external bool getBool(); +} + +extension Ext5 on C5 { + external String getString(); + external int getNumber(); + external bool getBool(); +} + +main() { + eval(r''' + globalThis.obj = { + getString: function () { + return "I'm JS String"; + }, + getNumber: function () { + return 42; + }, + getBool: function () { + return true; + } + }; + '''); + + C1 c1 = globalContext["obj"] as C1; + Expect.equals("I'm JS String", c1.getString()); + Expect.equals(42, c1.getNumber()); + Expect.equals(true, c1.getBool()); + + C2 c2 = globalContext["obj"] as C2; + Expect.equals("I'm JS String", c2.getString()); + Expect.equals(42, c2.getNumber()); + Expect.equals(true, c2.getBool()); + + C3 c3 = globalContext["obj"] as C3; + Expect.equals("I'm JS String", c3.getString()); + Expect.equals(42, c3.getNumber()); + Expect.equals(true, c3.getBool()); + + C4 c4 = globalContext["obj"] as C4; + Expect.equals("I'm JS String", c4.getString()); + Expect.equals(42, c4.getNumber()); + Expect.equals(true, c4.getBool()); + + C5 c5 = globalContext["obj"] as C5; + Expect.equals("I'm JS String", c5.getString()); + Expect.equals(42, c5.getNumber()); + Expect.equals(true, c5.getBool()); +} diff --git a/LibTest/js_interop/staticInterop_A04_t01.dart b/LibTest/js_interop/staticInterop_A04_t01.dart new file mode 100644 index 0000000000..746bef4735 --- /dev/null +++ b/LibTest/js_interop/staticInterop_A04_t01.dart @@ -0,0 +1,58 @@ +// Copyright (c) 2025, 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. + +/// @assertion `staticInterop` enables the JS annotated class to be treated as +/// a "static" interop class. +/// +/// These classes implicitly all erase to the internal interceptor +/// `JavaScriptObject`, so they can be freely casted to and from other +/// [staticInterop] types, `dart:html` types, and `JSObject` from +/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to +/// [staticInterop] types, but the reverse can fail if the underlying value is a +/// `@Native`-reserved type (like `dart:html` types). +/// +/// [staticInterop] classes have the following restrictions: +/// - They must contain a [JS] annotation, either from this library or from +/// `dart:js_interop`. +/// - They should not contain any instance members, inherited or otherwise, and +/// should instead use static extension members, which can be external or +/// non-external. +/// - They can only contain factories and `static` members. They can be +/// combined with [anonymous] to make external factories create new +/// JavaScript object literals instead. +/// - They should not implement, extend, or mixin non-[staticInterop] classes +/// and vice-versa. +/// - The annotation should only be applied to non-mixin classes and no other +/// declarations. +/// +/// @description Checks that it is a compile-time error if a class without +/// `@staticInterop` annotation extends or implements one with this annotation. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; + +@staticInterop +@JS() +class A {} + +@staticInterop +@JS() +abstract interface class I {} + +@JS() +class C1 extends A {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@JS() +class C2 implements I {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +main() { + print(C1); + print(C2); +} diff --git a/LibTest/js_interop/staticInterop_A04_t02.dart b/LibTest/js_interop/staticInterop_A04_t02.dart new file mode 100644 index 0000000000..192a446d29 --- /dev/null +++ b/LibTest/js_interop/staticInterop_A04_t02.dart @@ -0,0 +1,67 @@ +// Copyright (c) 2025, 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. + +/// @assertion `staticInterop` enables the JS annotated class to be treated as +/// a "static" interop class. +/// +/// These classes implicitly all erase to the internal interceptor +/// `JavaScriptObject`, so they can be freely casted to and from other +/// [staticInterop] types, `dart:html` types, and `JSObject` from +/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to +/// [staticInterop] types, but the reverse can fail if the underlying value is a +/// `@Native`-reserved type (like `dart:html` types). +/// +/// [staticInterop] classes have the following restrictions: +/// - They must contain a [JS] annotation, either from this library or from +/// `dart:js_interop`. +/// - They should not contain any instance members, inherited or otherwise, and +/// should instead use static extension members, which can be external or +/// non-external. +/// - They can only contain factories and `static` members. They can be +/// combined with [anonymous] to make external factories create new +/// JavaScript object literals instead. +/// - They should not implement, extend, or mixin non-[staticInterop] classes +/// and vice-versa. +/// - The annotation should only be applied to non-mixin classes and no other +/// declarations. +/// +/// @description Checks that it is a compile-time error if a class annotated +/// with `@staticInterop` annotation extends, mixin or implements one without +/// this annotation. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; + +class A {} + +mixin class M {} + +abstract interface class I {} + +@staticInterop +@JS() +class C1 extends A {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +class C2 with M {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +class C3 implements I {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +main() { + print(C1); + print(C2); + print(C3); +} diff --git a/LibTest/js_interop/staticInterop_A04_t03.dart b/LibTest/js_interop/staticInterop_A04_t03.dart new file mode 100644 index 0000000000..247edc3e67 --- /dev/null +++ b/LibTest/js_interop/staticInterop_A04_t03.dart @@ -0,0 +1,82 @@ +// Copyright (c) 2025, 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. + +/// @assertion `staticInterop` enables the JS annotated class to be treated as +/// a "static" interop class. +/// +/// These classes implicitly all erase to the internal interceptor +/// `JavaScriptObject`, so they can be freely casted to and from other +/// [staticInterop] types, `dart:html` types, and `JSObject` from +/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to +/// [staticInterop] types, but the reverse can fail if the underlying value is a +/// `@Native`-reserved type (like `dart:html` types). +/// +/// [staticInterop] classes have the following restrictions: +/// - They must contain a [JS] annotation, either from this library or from +/// `dart:js_interop`. +/// - They should not contain any instance members, inherited or otherwise, and +/// should instead use static extension members, which can be external or +/// non-external. +/// - They can only contain factories and `static` members. They can be +/// combined with [anonymous] to make external factories create new +/// JavaScript object literals instead. +/// - They should not implement, extend, or mixin non-[staticInterop] classes +/// and vice-versa. +/// - The annotation should only be applied to non-mixin classes and no other +/// declarations. +/// +/// @description Checks that a class annotated with `@staticInterop` can extend, +/// or implement another class/mixin annotated with `@staticInterop`. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import '../../Utils/expect.dart'; +import 'js_utils.dart'; + +@staticInterop +@JS() +abstract class A {} + +extension Ext on A { + external String getString(); + external int getNumber(); + external bool getBool(); +} + +@staticInterop +@JS() +class C1 extends A { +} + +@staticInterop +@JS() +class C2 implements A { +} + +main() { + eval(r''' + globalThis.obj = { + getString: function () { + return "I'm JS String"; + }, + getNumber: function () { + return 42; + }, + getBool: function () { + return true; + } + }; + '''); + + C1 c1 = globalContext["obj"] as C1; + Expect.equals("I'm JS String", c1.getString()); + Expect.equals(42, c1.getNumber()); + Expect.equals(true, c1.getBool()); + + C2 c2 = globalContext["obj"] as C2; + Expect.equals("I'm JS String", c2.getString()); + Expect.equals(42, c2.getNumber()); + Expect.equals(true, c2.getBool()); +} diff --git a/LibTest/js_interop/staticInterop_A05_t01.dart b/LibTest/js_interop/staticInterop_A05_t01.dart new file mode 100644 index 0000000000..7055934d79 --- /dev/null +++ b/LibTest/js_interop/staticInterop_A05_t01.dart @@ -0,0 +1,85 @@ +// Copyright (c) 2025, 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. + +/// @assertion `staticInterop` enables the JS annotated class to be treated as +/// a "static" interop class. +/// +/// These classes implicitly all erase to the internal interceptor +/// `JavaScriptObject`, so they can be freely casted to and from other +/// [staticInterop] types, `dart:html` types, and `JSObject` from +/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to +/// [staticInterop] types, but the reverse can fail if the underlying value is a +/// `@Native`-reserved type (like `dart:html` types). +/// +/// [staticInterop] classes have the following restrictions: +/// - They must contain a [JS] annotation, either from this library or from +/// `dart:js_interop`. +/// - They should not contain any instance members, inherited or otherwise, and +/// should instead use static extension members, which can be external or +/// non-external. +/// - They can only contain factories and `static` members. They can be +/// combined with [anonymous] to make external factories create new +/// JavaScript object literals instead. +/// - They should not implement, extend, or mixin non-[staticInterop] classes +/// and vice-versa. +/// - The annotation should only be applied to non-mixin classes and no other +/// declarations. +/// +/// @description Checks that it is a compile-time error if a mixin or mixin +/// class is annotated with `@staticInterop`. +/// @author sgrekhov22@gmail.com +/// @issue 61125 + +import 'dart:js_interop'; + +@staticInterop +@JS() +mixin M1 {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +base mixin M2 {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +mixin class M3 {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +abstract mixin class M4 {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +base mixin class M5 {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +@staticInterop +@JS() +abstract base mixin class M6 {} +// ^^ +// [analyzer] unspecified +// [web] unspecified + +main() { + print(M1); + print(M2); + print(M3); + print(M4); + print(M5); + print(M6); +}