Skip to content

Commit a2312f4

Browse files
rakudramaCommit Queue
authored andcommitted
[js_interop] Use stub for dart2js only when method has annotations
In order to support dart2js pragma annotations on external js_interop methods, add the ability to generate stubs for getters, setters and methods. Create stubs for dart2js only when the method has annotations. Methods can only be handled by stubs when there are a fixed number of positional arguments, Move from using a `builder` to using a slightly more general 'treatement' that can also replace the Procedure's function body. Issue: #60746 Change-Id: I7482f09d430448001d712dd4645d23f78910920d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/429203 Reviewed-by: Srujan Gaddam <[email protected]> Commit-Queue: Stephen Adams <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]>
1 parent d6cc4c3 commit a2312f4

15 files changed

+1041
-201
lines changed

pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart

Lines changed: 246 additions & 190 deletions
Large diffs are not rendered by default.

pkg/front_end/testcases/dart2js/extension_types/external.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extension type B._(A a) {
4444
external static B get staticProperty;
4545

4646
external static void set staticProperty(B b);
47+
48+
external B methodWithOptionalArgument([B? b]);
4749
}
4850

4951
void method(A a) {
@@ -63,4 +65,6 @@ void method(A a) {
6365
b1 = B.staticGetter;
6466
B.staticSetter = b2;
6567
B.staticProperty = B.staticProperty;
66-
}
68+
b2 = b1.methodWithOptionalArgument(b2);
69+
b1 = b2.methodWithOptionalArgument();
70+
}

pkg/front_end/testcases/dart2js/extension_types/external.dart.strong.expect

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ extension type B(self::A a) {
3737
static set staticSetter = set self::B|staticSetter;
3838
static get staticProperty = get self::B|staticProperty;
3939
static set staticProperty = set self::B|staticProperty;
40+
method methodWithOptionalArgument = self::B|methodWithOptionalArgument;
41+
method tearoff methodWithOptionalArgument = self::B|get#methodWithOptionalArgument;
4042
constructor _ = self::B|constructor#_;
4143
constructor tearoff _ = self::B|constructor#_#_#tearOff;
4244
constructor • = self::B|constructor#;
@@ -76,6 +78,9 @@ external static extension-type-member get B|staticGetter() → self::B% /* erasu
7678
external static extension-type-member set B|staticSetter(self::B% /* erasure=self::A, declared=! */ b) → void;
7779
external static extension-type-member get B|staticProperty() → self::B% /* erasure=self::A, declared=! */;
7880
external static extension-type-member set B|staticProperty(self::B% /* erasure=self::A, declared=! */ b) → void;
81+
external static extension-type-member method B|methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this, [self::B? /* erasure=self::A? */ b = #C1]) → self::B% /* erasure=self::A, declared=! */;
82+
static extension-type-member method B|get#methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this) → ([self::B? /* erasure=self::A? */]) → self::B% /* erasure=self::A, declared=! */
83+
return ([self::B? /* erasure=self::A? */ b = #C1]) → self::B% /* erasure=self::A, declared=! */ => self::B|methodWithOptionalArgument(#this, b);
7984
static method method(self::A a) → void {
8085
self::B% /* erasure=self::A, declared=! */ b1 = self::B|constructor#(a);
8186
self::B% /* erasure=self::A, declared=! */ b2 = self::B|constructor#named(0);
@@ -93,6 +98,8 @@ static method method(self::A a) → void {
9398
b1 = self::B|staticGetter;
9499
self::B|staticSetter = b2;
95100
self::B|staticProperty = self::B|staticProperty;
101+
b2 = self::B|methodWithOptionalArgument(b1, b2);
102+
b1 = self::B|methodWithOptionalArgument(b2);
96103
}
97104

98105
constants {

pkg/front_end/testcases/dart2js/extension_types/external.dart.strong.modular.expect

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ extension type B(self::A a) {
3737
static set staticSetter = set self::B|staticSetter;
3838
static get staticProperty = get self::B|staticProperty;
3939
static set staticProperty = set self::B|staticProperty;
40+
method methodWithOptionalArgument = self::B|methodWithOptionalArgument;
41+
method tearoff methodWithOptionalArgument = self::B|get#methodWithOptionalArgument;
4042
constructor _ = self::B|constructor#_;
4143
constructor tearoff _ = self::B|constructor#_#_#tearOff;
4244
constructor • = self::B|constructor#;
@@ -76,6 +78,9 @@ external static extension-type-member get B|staticGetter() → self::B% /* erasu
7678
external static extension-type-member set B|staticSetter(self::B% /* erasure=self::A, declared=! */ b) → void;
7779
external static extension-type-member get B|staticProperty() → self::B% /* erasure=self::A, declared=! */;
7880
external static extension-type-member set B|staticProperty(self::B% /* erasure=self::A, declared=! */ b) → void;
81+
external static extension-type-member method B|methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this, [self::B? /* erasure=self::A? */ b = #C1]) → self::B% /* erasure=self::A, declared=! */;
82+
static extension-type-member method B|get#methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this) → ([self::B? /* erasure=self::A? */]) → self::B% /* erasure=self::A, declared=! */
83+
return ([self::B? /* erasure=self::A? */ b = #C1]) → self::B% /* erasure=self::A, declared=! */ => self::B|methodWithOptionalArgument(#this, b);
7984
static method method(self::A a) → void {
8085
self::B% /* erasure=self::A, declared=! */ b1 = self::B|constructor#(a);
8186
self::B% /* erasure=self::A, declared=! */ b2 = self::B|constructor#named(0);
@@ -93,6 +98,8 @@ static method method(self::A a) → void {
9398
b1 = self::B|staticGetter;
9499
self::B|staticSetter = b2;
95100
self::B|staticProperty = self::B|staticProperty;
101+
b2 = self::B|methodWithOptionalArgument(b1, b2);
102+
b1 = self::B|methodWithOptionalArgument(b2);
96103
}
97104

98105
constants {

pkg/front_end/testcases/dart2js/extension_types/external.dart.strong.outline.expect

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ extension type B(self::A a) {
3636
static set staticSetter = set self::B|staticSetter;
3737
static get staticProperty = get self::B|staticProperty;
3838
static set staticProperty = set self::B|staticProperty;
39+
method methodWithOptionalArgument = self::B|methodWithOptionalArgument;
40+
method tearoff methodWithOptionalArgument = self::B|get#methodWithOptionalArgument;
3941
constructor _ = self::B|constructor#_;
4042
constructor tearoff _ = self::B|constructor#_#_#tearOff;
4143
constructor • = self::B|constructor#;
@@ -73,6 +75,9 @@ external static extension-type-member get B|staticGetter() → self::B% /* erasu
7375
external static extension-type-member set B|staticSetter(self::B% /* erasure=self::A, declared=! */ b) → void;
7476
external static extension-type-member get B|staticProperty() → self::B% /* erasure=self::A, declared=! */;
7577
external static extension-type-member set B|staticProperty(self::B% /* erasure=self::A, declared=! */ b) → void;
78+
external static extension-type-member method B|methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this, [self::B? /* erasure=self::A? */ b]) → self::B% /* erasure=self::A, declared=! */;
79+
static extension-type-member method B|get#methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this) → ([self::B? /* erasure=self::A? */]) → self::B% /* erasure=self::A, declared=! */
80+
return ([self::B? /* erasure=self::A? */ b]) → self::B% /* erasure=self::A, declared=! */ => self::B|methodWithOptionalArgument(#this, b);
7681
static method method(self::A a) → void
7782
;
7883

@@ -82,4 +87,4 @@ Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:5:2 ->
8287
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:10:2 -> InstanceConstant(const JS{JS.name: null})
8388
Evaluated: StaticGet @ org-dartlang-testcase:///external.dart:11:2 -> InstanceConstant(const _StaticInterop{})
8489
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:14:2 -> InstanceConstant(const JS{JS.name: null})
85-
Extra constant evaluation: evaluated: 18, effectively constant: 4
90+
Extra constant evaluation: evaluated: 22, effectively constant: 4

pkg/front_end/testcases/dart2js/extension_types/external.dart.strong.transformed.expect

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ extension type B(self::A a) {
3939
static set staticSetter = set self::B|staticSetter;
4040
static get staticProperty = get self::B|staticProperty;
4141
static set staticProperty = set self::B|staticProperty;
42+
method methodWithOptionalArgument = self::B|methodWithOptionalArgument;
43+
method tearoff methodWithOptionalArgument = self::B|get#methodWithOptionalArgument;
4244
constructor _ = self::B|constructor#_;
4345
constructor tearoff _ = self::B|constructor#_#_#tearOff;
4446
constructor • = self::B|constructor#;
@@ -70,17 +72,17 @@ external static extension-type-member method B|get#getter(lowered final self::B%
7072
external static extension-type-member method B|set#setter(lowered final self::B% /* erasure=self::A, declared=! */ #this, self::B% /* erasure=self::A, declared=! */ b) → void;
7173
external static extension-type-member method B|get#property(lowered final self::B% /* erasure=self::A, declared=! */ #this) → self::B% /* erasure=self::A, declared=! */;
7274
external static extension-type-member method B|set#property(lowered final self::B% /* erasure=self::A, declared=! */ #this, self::B% /* erasure=self::A, declared=! */ b) → void;
73-
static extension-type-member get B|staticField() → self::A
74-
return js_2::_getPropertyTrustType<dynamic>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticField") as{TypeError,ForDynamic} self::A;
75+
external static extension-type-member get B|staticField() → self::A;
7576
external static extension-type-member set B|staticField(synthesized self::A #externalFieldValue) → void;
7677
external static extension-type-member method B|staticMethod() → self::A;
7778
external static extension-type-member method B|staticGenericMethod<T extends self::B% /* erasure=self::A, declared=! */>(self::B|staticGenericMethod::T% t) → self::B|staticGenericMethod::T%;
78-
static extension-type-member get B|staticGetter() → self::B% /* erasure=self::A, declared=! */
79-
return js_2::_getPropertyTrustType<dynamic>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticGetter") as{TypeError,ForDynamic} self::B% /* erasure=self::A, declared=! */;
79+
external static extension-type-member get B|staticGetter() → self::B% /* erasure=self::A, declared=! */;
8080
external static extension-type-member set B|staticSetter(self::B% /* erasure=self::A, declared=! */ b) → void;
81-
static extension-type-member get B|staticProperty() → self::B% /* erasure=self::A, declared=! */
82-
return js_2::_getPropertyTrustType<dynamic>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticProperty") as{TypeError,ForDynamic} self::B% /* erasure=self::A, declared=! */;
81+
external static extension-type-member get B|staticProperty() → self::B% /* erasure=self::A, declared=! */;
8382
external static extension-type-member set B|staticProperty(self::B% /* erasure=self::A, declared=! */ b) → void;
83+
external static extension-type-member method B|methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this, [self::B? /* erasure=self::A? */ b = #C1]) → self::B% /* erasure=self::A, declared=! */;
84+
static extension-type-member method B|get#methodWithOptionalArgument(lowered final self::B% /* erasure=self::A, declared=! */ #this) → ([self::B? /* erasure=self::A? */]) → self::B% /* erasure=self::A, declared=! */
85+
return ([self::B? /* erasure=self::A? */ b = #C1]) → self::B% /* erasure=self::A, declared=! */ => js_2::_callMethodUnchecked1<self::B% /* erasure=self::A, declared=! */>(#this, "methodWithOptionalArgument", b);
8486
static method method(self::A a) → void {
8587
self::B% /* erasure=self::A, declared=! */ b1 = js_2::_callConstructorUnchecked1<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), a);
8688
self::B% /* erasure=self::A, declared=! */ b2 = js_2::_callConstructorUnchecked1<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), 0);
@@ -91,13 +93,15 @@ static method method(self::A a) → void {
9193
b1 = js_2::getProperty<self::B% /* erasure=self::A, declared=! */>(b2, "getter");
9294
js_2::_setPropertyUnchecked<self::B% /* erasure=self::A, declared=! */>(b1, "setter", b2);
9395
js_2::_setPropertyUnchecked<self::B% /* erasure=self::A, declared=! */>(b1, "property", js_2::getProperty<self::B% /* erasure=self::A, declared=! */>(b2, "property"));
94-
a = self::B|staticField;
96+
a = js_2::getProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticField");
9597
js_2::_setPropertyUnchecked<self::A>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticField", a);
9698
a = js_2::_callMethodUnchecked0<self::A>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticMethod");
9799
b2 = js_2::_callMethodUnchecked1<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticGenericMethod", b2);
98-
b1 = self::B|staticGetter;
100+
b1 = js_2::getProperty<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticGetter");
99101
js_2::_setPropertyUnchecked<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticSetter", b2);
100-
js_2::_setPropertyUnchecked<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticProperty", self::B|staticProperty);
102+
js_2::_setPropertyUnchecked<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticProperty", js_2::getProperty<self::B% /* erasure=self::A, declared=! */>(js_2::_getPropertyTrustType<core::Object>(_js2::staticInteropGlobalContext, "B"), "staticProperty"));
103+
b2 = js_2::_callMethodUnchecked1<self::B% /* erasure=self::A, declared=! */>(b1, "methodWithOptionalArgument", b2);
104+
b1 = js_2::_callMethodUnchecked0<self::B% /* erasure=self::A, declared=! */>(b2, "methodWithOptionalArgument");
101105
}
102106

103107
constants {

pkg/front_end/testcases/dart2js/extension_types/external.dart.textual_outline.expect

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extension type B._(A a) {
2626
external static void set staticSetter(B b);
2727
external static B get staticProperty;
2828
external static void set staticProperty(B b);
29+
external B methodWithOptionalArgument([B? b]);
2930
}
3031

3132
void method(A a) {}

pkg/front_end/testcases/dart2js/extension_types/external.dart.textual_outline_modelled.expect

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extension type B._(A a) {
1515
external B.named(int i);
1616
external B get getter;
1717
external B get property;
18+
external B methodWithOptionalArgument([B? b]);
1819
external T genericMethod<T extends B>(T t);
1920
external static A staticField;
2021
external static A staticMethod();
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// This test is the same as `external.dart`, except all external members have a
6+
// `@pragma` annotation.
7+
8+
@JS()
9+
library static_interop;
10+
11+
import 'dart:js_interop';
12+
13+
@JS()
14+
@staticInterop
15+
class A {}
16+
17+
@JS()
18+
extension type B._(A a) {
19+
@annotation
20+
external B(A a);
21+
22+
@annotation
23+
external B.named(int i);
24+
25+
@annotation
26+
external A field;
27+
28+
@annotation
29+
external A method();
30+
31+
@annotation
32+
external T genericMethod<T extends B>(T t);
33+
34+
@annotation
35+
external B get getter;
36+
37+
@annotation
38+
external void set setter(B b);
39+
40+
@annotation
41+
external B get property;
42+
43+
@annotation
44+
external void set property(B b);
45+
46+
@annotation
47+
external static A staticField;
48+
49+
@annotation
50+
external static A staticMethod();
51+
52+
@annotation
53+
external static T staticGenericMethod<T extends B>(T t);
54+
55+
@annotation
56+
external static B get staticGetter;
57+
58+
@annotation
59+
external static void set staticSetter(B b);
60+
61+
@annotation
62+
external static B get staticProperty;
63+
64+
@annotation
65+
external static void set staticProperty(B b);
66+
67+
@annotation
68+
external B methodWithOptionalArgument([B? b]);
69+
}
70+
71+
void method(A a) {
72+
B b1 = new B(a);
73+
B b2 = new B.named(0);
74+
a = b1.field;
75+
b1.field = a;
76+
a = b1.method();
77+
b2 = b2.genericMethod(b2);
78+
b1 = b2.getter;
79+
b1.setter = b2;
80+
b1.property = b2.property;
81+
a = B.staticField;
82+
B.staticField = a;
83+
a = B.staticMethod();
84+
b2 = B.staticGenericMethod(b2);
85+
b1 = B.staticGetter;
86+
B.staticSetter = b2;
87+
B.staticProperty = B.staticProperty;
88+
b2 = b1.methodWithOptionalArgument(b2);
89+
b1 = b2.methodWithOptionalArgument();
90+
}
91+
92+
const annotation = pragma('a pragma');

0 commit comments

Comments
 (0)