Skip to content

Commit 8f28795

Browse files
committed
[stable] [dart2js] Erase static interop type in static invocation
Static invocations of external factories are casted so that the result, which is a @staticInterop type, can be treated as the erased type instead. This CL fixes the issue where the type that it was casted to was never replaced with the erased type. Change-Id: Ic6eb529349ea2b5c42f91c2740d501d4f81bc38e Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/323505 Cherry-pick-request: #53579 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/327120 Reviewed-by: Sigmund Cherem <[email protected]>
1 parent 9bf2f88 commit 8f28795

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 3.1.3
2+
3+
This is a patch release that:
4+
5+
- Fixes a bug in dart2js which would cause the compiler to crash when using
6+
`@staticInterop` `@anonymous` factory constructors with type parameters (see
7+
issue [#53579] for more details).
8+
9+
[#53579]: https://github.com/dart-lang/sdk/issues/53579
10+
111
## 3.1.2 - 2023-09-13
212

313
This is a patch release that:

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ class _TypeSubstitutor extends ReplacementVisitor {
128128
}
129129
}
130130

131-
/// Erases usage of `@JS` classes that are annotated with `@staticInterop` in
132-
/// favor of `JavaScriptObject`.
131+
/// Erases usage of `@JS` classes that are annotated with `@staticInterop`.
133132
class StaticInteropClassEraser extends Transformer {
134133
final CloneVisitorNotMembers _cloner = CloneVisitorNotMembers();
135134
late final _StaticInteropConstantReplacer _constantReplacer;
@@ -252,7 +251,7 @@ class StaticInteropClassEraser extends Transformer {
252251
//
253252
// In order to circumvent this, we introduce a new static method that
254253
// clones the factory body and has a return type of
255-
// `JavaScriptObject`. Invocations of the factory are turned into
254+
// the erased type. Invocations of the factory are turned into
256255
// invocations of the static method. The original factory is still kept
257256
// in order to make modular compilations work.
258257
_findOrCreateFactoryStub(node);
@@ -283,7 +282,7 @@ class StaticInteropClassEraser extends Transformer {
283282
@override
284283
TreeNode visitConstructorInvocation(ConstructorInvocation node) {
285284
if (hasStaticInteropAnnotation(node.target.enclosingClass)) {
286-
// Add a cast so that the result gets typed as `JavaScriptObject`.
285+
// Add a cast so that the result gets typed as the erased type.
287286
var newInvocation = super.visitConstructorInvocation(node) as Expression;
288287
return AsExpression(
289288
newInvocation,
@@ -314,10 +313,12 @@ class StaticInteropClassEraser extends Transformer {
314313
return StaticInvocation(stub, args, isConst: node.isConst)
315314
..fileOffset = node.fileOffset;
316315
} else {
317-
// Add a cast so that the result gets typed as `JavaScriptObject`.
316+
// Add a cast so that the result gets typed as the erased type.
318317
var newInvocation = super.visitStaticInvocation(node) as Expression;
319318
return AsExpression(
320-
newInvocation, node.target.function.returnType as InterfaceType)
319+
newInvocation,
320+
_eraseStaticInteropType(
321+
node.target.function.returnType as InterfaceType))
321322
..fileOffset = newInvocation.fileOffset;
322323
}
323324
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) 2023, 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+
// Test type parameters on @staticInterop factories.
6+
7+
import 'package:js/js.dart';
8+
import 'package:expect/minitest.dart';
9+
10+
@JS()
11+
@staticInterop
12+
class Array<T, U extends String> {
13+
external factory Array(T t, U u);
14+
factory Array.nonExternal(T t, U u) => Array(t, u);
15+
}
16+
17+
extension on Array {
18+
external Object operator [](int index);
19+
}
20+
21+
@JS()
22+
@staticInterop
23+
@anonymous
24+
class Anonymous<T, U extends String> {
25+
external factory Anonymous({T? t, U? u});
26+
factory Anonymous.nonExternal({T? t, U? u}) => Anonymous(t: t, u: u);
27+
}
28+
29+
extension AnonymousExtension on Anonymous {
30+
external Object? get t;
31+
external Object? get u;
32+
}
33+
34+
void main() {
35+
final arr1 = Array(true, '');
36+
expect(arr1[0], true);
37+
expect(arr1[1], '');
38+
final arr2 = Array<bool, String>(false, '');
39+
expect(arr2[0], false);
40+
expect(arr2[1], '');
41+
final anon1 = Anonymous(t: true, u: '');
42+
expect(anon1.t, true);
43+
expect(anon1.u, '');
44+
final anon2 = Anonymous<bool, String>(t: false, u: '');
45+
expect(anon2.t, false);
46+
expect(anon2.u, '');
47+
}

0 commit comments

Comments
 (0)