From 20a3f939538c5875f12c6effc2eafff956d4e888 Mon Sep 17 00:00:00 2001 From: "Sergey G. Grekhov" Date: Mon, 14 Jul 2025 13:23:24 +0300 Subject: [PATCH 1/3] #3180. Add `anonymous` annotation tests --- LibTest/js_interop/anonymous_A01_t01.dart | 41 ++++++++++++++ LibTest/js_interop/anonymous_A01_t02.dart | 41 ++++++++++++++ LibTest/js_interop/anonymous_A02_t01.dart | 34 +++++++++++ LibTest/js_interop/anonymous_A03_t01.dart | 69 +++++++++++++++++++++++ LibTest/js_interop/anonymous_A03_t02.dart | 47 +++++++++++++++ LibTest/js_interop/anonymous_A03_t03.dart | 48 ++++++++++++++++ LibTest/js_interop/anonymous_A03_t04.dart | 49 ++++++++++++++++ LibTest/js_interop/anonymous_A03_t05.dart | 37 ++++++++++++ 8 files changed, 366 insertions(+) create mode 100644 LibTest/js_interop/anonymous_A01_t01.dart create mode 100644 LibTest/js_interop/anonymous_A01_t02.dart create mode 100644 LibTest/js_interop/anonymous_A02_t01.dart create mode 100644 LibTest/js_interop/anonymous_A03_t01.dart create mode 100644 LibTest/js_interop/anonymous_A03_t02.dart create mode 100644 LibTest/js_interop/anonymous_A03_t03.dart create mode 100644 LibTest/js_interop/anonymous_A03_t04.dart create mode 100644 LibTest/js_interop/anonymous_A03_t05.dart diff --git a/LibTest/js_interop/anonymous_A01_t01.dart b/LibTest/js_interop/anonymous_A01_t01.dart new file mode 100644 index 0000000000..4de1d75a54 --- /dev/null +++ b/LibTest/js_interop/anonymous_A01_t01.dart @@ -0,0 +1,41 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that invoking the constructor desugars to creating a +/// JavaScript object literal with name-value pairs corresponding to the +/// parameter names and values. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import '../../Utils/expect.dart'; +import 'js_utils.dart'; + +@anonymous +@staticInterop +@JS() +class C { + external factory C({String s, int n, bool b}); +} + +main() { + var c = C(s: "s value", n: 42, b: true); + globalContext["c"] = c.jsify(); + eval(r''' + globalThis.resS = globalThis.c.s; + globalThis.resN = globalThis.c.n; + globalThis.resB = globalThis.c.b; + '''); + Expect.equals("s value", (globalContext["resS"] as JSString).toDart); + Expect.equals(42, (globalContext["resN"] as JSNumber).toDartInt); + Expect.equals(true, (globalContext["resB"] as JSBoolean).toDart); +} diff --git a/LibTest/js_interop/anonymous_A01_t02.dart b/LibTest/js_interop/anonymous_A01_t02.dart new file mode 100644 index 0000000000..cbb88fdd86 --- /dev/null +++ b/LibTest/js_interop/anonymous_A01_t02.dart @@ -0,0 +1,41 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that invoking the constructor desugars to creating a +/// JavaScript object literal with name-value pairs corresponding to the +/// parameter names and values. Test a named factory constructor. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import '../../Utils/expect.dart'; +import 'js_utils.dart'; + +@anonymous +@staticInterop +@JS() +class C { + external factory C.f({String s, int n, bool b}); +} + +main() { + var c = C.f(s: "s value", n: 42, b: true); + globalContext["c"] = c.jsify(); + eval(r''' + globalThis.resS = globalThis.c.s; + globalThis.resN = globalThis.c.n; + globalThis.resB = globalThis.c.b; + '''); + Expect.equals("s value", (globalContext["resS"] as JSString).toDart); + Expect.equals(42, (globalContext["resN"] as JSNumber).toDartInt); + Expect.equals(true, (globalContext["resB"] as JSBoolean).toDart); +} diff --git a/LibTest/js_interop/anonymous_A02_t01.dart b/LibTest/js_interop/anonymous_A02_t01.dart new file mode 100644 index 0000000000..b7bed7e1a5 --- /dev/null +++ b/LibTest/js_interop/anonymous_A02_t01.dart @@ -0,0 +1,34 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that it is not an error if a class annotated with +/// [anonymous] has no unnamed factory constructor. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; + +@anonymous +@staticInterop +@JS() +class C1 { +} + +@anonymous +@staticInterop +@JS() +abstract class C2 { +} + +main() { + print(C1); + print(C2); +} diff --git a/LibTest/js_interop/anonymous_A03_t01.dart b/LibTest/js_interop/anonymous_A03_t01.dart new file mode 100644 index 0000000000..761e83fde1 --- /dev/null +++ b/LibTest/js_interop/anonymous_A03_t01.dart @@ -0,0 +1,69 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that it is a warning if an extension type is annotated +/// with [anonymous]. +/// @author sgrekhov22@gmail.com +/// @issue 61018 + +import 'dart:js_interop'; + +@anonymous +@JS() +extension type Ext1(JSObject _) { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified +} + +@anonymous +@JS() +extension type Ext2.x(JSObject _) { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified + + external factory Ext2({String id}); +} + +@anonymous +@staticInterop +@JS() +extension type Ext3._(JSObject _) { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified + external factory Ext3({String id}); +} + +@anonymous +extension type Ext4(JSObject id) { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified +} + +@anonymous +extension type Ext5.x(JSObject id) { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified + factory Ext5(JSObject id) = Ext5.x; +} + +main() { + print(Ext1); + print(Ext2); + print(Ext3); + print(Ext4); + print(Ext5); +} diff --git a/LibTest/js_interop/anonymous_A03_t02.dart b/LibTest/js_interop/anonymous_A03_t02.dart new file mode 100644 index 0000000000..ae901c229e --- /dev/null +++ b/LibTest/js_interop/anonymous_A03_t02.dart @@ -0,0 +1,47 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that it is a compile-time error if an enum is annotated +/// with [anonymous]. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; + +@anonymous +@staticInterop +@JS() +enum E1 { +// ^^ +// [analyzer] unspecified +// [web] unspecified + e0; +} + +@anonymous +@JS() +enum E2 { +// ^^ +// [analyzer] unspecified +// [web] unspecified + e0; +} + +@anonymous +enum E3 { // Ok, just no-op + e0; +} + +main() { + print(E1); + print(E2); + print(E3); +} diff --git a/LibTest/js_interop/anonymous_A03_t03.dart b/LibTest/js_interop/anonymous_A03_t03.dart new file mode 100644 index 0000000000..decf4cfd4c --- /dev/null +++ b/LibTest/js_interop/anonymous_A03_t03.dart @@ -0,0 +1,48 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that it is a compile-time error if a mixin is annotated +/// with [anonymous]. +/// @author sgrekhov22@gmail.com +/// @issue 61018 + +import 'dart:js_interop'; + +@anonymous +@JS() +mixin M1 { +// ^^ +// [analyzer] unspecified +// [web] unspecified +} + +@anonymous +@staticInterop +@JS() +mixin M2 { +// ^^ +// [analyzer] unspecified +// [web] unspecified +} + +@anonymous +mixin M3 { +// ^^ +// [analyzer] unspecified +// [web] unspecified +} + +main() { + print(M1); + print(M2); + print(M3); +} diff --git a/LibTest/js_interop/anonymous_A03_t04.dart b/LibTest/js_interop/anonymous_A03_t04.dart new file mode 100644 index 0000000000..55ced9f92f --- /dev/null +++ b/LibTest/js_interop/anonymous_A03_t04.dart @@ -0,0 +1,49 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that it is a compile-time error if an extension +/// is annotated with `@anonymous`. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; + +class C1 { + C1.n({String id = ""}); + factory C1({String id}) = C1.n; +} + +@anonymous +@JS() +extension Ext1 on C1 { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified +} + +@anonymous +@staticInterop +@JS() +class C2 { + external factory C2({String id}); +} + +@anonymous +extension Ext2 on C2 { +// ^^^^ +// [analyzer] unspecified +// [web] unspecified +} + +main() { + print(C1); + print(C2); +} diff --git a/LibTest/js_interop/anonymous_A03_t05.dart b/LibTest/js_interop/anonymous_A03_t05.dart new file mode 100644 index 0000000000..28aa6fcf65 --- /dev/null +++ b/LibTest/js_interop/anonymous_A03_t05.dart @@ -0,0 +1,37 @@ +// 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 An annotation that indicates a JS annotated class is structural +/// and does not have a known JavaScript prototype. +/// +/// A class marked with [anonymous] must have an unnamed factory constructor +/// with no positional arguments, only named arguments. Invoking the constructor +/// desugars to creating a JavaScript object literal with name-value pairs +/// corresponding to the parameter names and values. +/// +/// @description Checks that it is a compile-time error if not a type is marked +/// with [anonymous]. +/// @author sgrekhov22@gmail.com + +import 'dart:js_interop'; + +@anonymous +void foo() {} + +@anonymous +@JS() +int get bar => 42; + +@anonymous +@JS() +String baz = "baz"; + +@anonymous +void set qux(int _) {} + +main() { + print(foo); + print(bar); + print(baz); +} From c903010280be7129a9d24e55f2009abeda56b8da Mon Sep 17 00:00:00 2001 From: "Sergey G. Grekhov" Date: Mon, 14 Jul 2025 17:19:55 +0300 Subject: [PATCH 2/3] Add missing errors expectations --- LibTest/js_interop/anonymous_A03_t05.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/LibTest/js_interop/anonymous_A03_t05.dart b/LibTest/js_interop/anonymous_A03_t05.dart index 28aa6fcf65..bdebbc1eff 100644 --- a/LibTest/js_interop/anonymous_A03_t05.dart +++ b/LibTest/js_interop/anonymous_A03_t05.dart @@ -13,22 +13,35 @@ /// @description Checks that it is a compile-time error if not a type is marked /// with [anonymous]. /// @author sgrekhov22@gmail.com +/// @issue 61109 import 'dart:js_interop'; @anonymous void foo() {} +// ^^^ +// [analyzer] unspecified +// [web] unspecified @anonymous @JS() int get bar => 42; +// ^^^ +// [analyzer] unspecified +// [web] unspecified @anonymous @JS() String baz = "baz"; +// ^^^ +// [analyzer] unspecified +// [web] unspecified @anonymous void set qux(int _) {} +// ^^^ +// [analyzer] unspecified +// [web] unspecified main() { print(foo); From 8d4fc36a54a3a374ca7c00a5ed885718c9d23362 Mon Sep 17 00:00:00 2001 From: "Sergey G. Grekhov" Date: Tue, 15 Jul 2025 11:08:00 +0300 Subject: [PATCH 3/3] Implement review recommendations --- LibTest/js_interop/anonymous_A01_t01.dart | 2 +- LibTest/js_interop/anonymous_A01_t02.dart | 2 +- LibTest/js_interop/anonymous_A02_t01.dart | 13 +++++++++---- LibTest/js_interop/anonymous_A03_t02.dart | 6 +++++- LibTest/js_interop/anonymous_A03_t05.dart | 2 +- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/LibTest/js_interop/anonymous_A01_t01.dart b/LibTest/js_interop/anonymous_A01_t01.dart index 4de1d75a54..556619c82d 100644 --- a/LibTest/js_interop/anonymous_A01_t01.dart +++ b/LibTest/js_interop/anonymous_A01_t01.dart @@ -29,7 +29,7 @@ class C { main() { var c = C(s: "s value", n: 42, b: true); - globalContext["c"] = c.jsify(); + globalContext["c"] = c as JSObject; eval(r''' globalThis.resS = globalThis.c.s; globalThis.resN = globalThis.c.n; diff --git a/LibTest/js_interop/anonymous_A01_t02.dart b/LibTest/js_interop/anonymous_A01_t02.dart index cbb88fdd86..bbdcdd40ff 100644 --- a/LibTest/js_interop/anonymous_A01_t02.dart +++ b/LibTest/js_interop/anonymous_A01_t02.dart @@ -29,7 +29,7 @@ class C { main() { var c = C.f(s: "s value", n: 42, b: true); - globalContext["c"] = c.jsify(); + globalContext["c"] = c as JSObject; eval(r''' globalThis.resS = globalThis.c.s; globalThis.resN = globalThis.c.n; diff --git a/LibTest/js_interop/anonymous_A02_t01.dart b/LibTest/js_interop/anonymous_A02_t01.dart index b7bed7e1a5..8da886e7f2 100644 --- a/LibTest/js_interop/anonymous_A02_t01.dart +++ b/LibTest/js_interop/anonymous_A02_t01.dart @@ -11,14 +11,15 @@ /// corresponding to the parameter names and values. /// /// @description Checks that it is not an error if a class annotated with -/// [anonymous] has no unnamed factory constructor. +/// [anonymous] has no unnamed factory constructor. Creating an instance creates +/// an empty JS object. /// @author sgrekhov22@gmail.com import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'js_utils.dart'; @anonymous -@staticInterop -@JS() class C1 { } @@ -29,6 +30,10 @@ abstract class C2 { } main() { - print(C1); + globalContext["o"] = C1().jsify(); + eval(r''' + globalThis.keys = Object.keys(globalThis.o); + '''); + jsExpectArrayEquals(JSArray(), globalContext["keys"]); print(C2); } diff --git a/LibTest/js_interop/anonymous_A03_t02.dart b/LibTest/js_interop/anonymous_A03_t02.dart index ae901c229e..9f9a94bd21 100644 --- a/LibTest/js_interop/anonymous_A03_t02.dart +++ b/LibTest/js_interop/anonymous_A03_t02.dart @@ -13,6 +13,7 @@ /// @description Checks that it is a compile-time error if an enum is annotated /// with [anonymous]. /// @author sgrekhov22@gmail.com +/// @issue 61115 import 'dart:js_interop'; @@ -36,7 +37,10 @@ enum E2 { } @anonymous -enum E3 { // Ok, just no-op +enum E3 { +// ^^ +// [analyzer] unspecified +// [web] unspecified e0; } diff --git a/LibTest/js_interop/anonymous_A03_t05.dart b/LibTest/js_interop/anonymous_A03_t05.dart index bdebbc1eff..9d54ecb27e 100644 --- a/LibTest/js_interop/anonymous_A03_t05.dart +++ b/LibTest/js_interop/anonymous_A03_t05.dart @@ -10,7 +10,7 @@ /// desugars to creating a JavaScript object literal with name-value pairs /// corresponding to the parameter names and values. /// -/// @description Checks that it is a compile-time error if not a type is marked +/// @description Checks that it is a compile-time error if a non-type is marked /// with [anonymous]. /// @author sgrekhov22@gmail.com /// @issue 61109