Skip to content

Commit 6dd9806

Browse files
srujzsCommit Queue
authored and
Commit Queue
committed
[stable][dart2js] Handle object literal constructors with no library @js annotation
Fixes #54801 Object literal constructors need to be explicitly handled when determining a member is JS interop or not in dart2js as it does not require any @js annotations. Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/349840 Cherry-pick-request: #55057 Change-Id: Ie178e29f8c4f5b032440b58c08eb81cf896bad70 Bug: #54801 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/355060 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Srujan Gaddam <srujzs@google.com>
1 parent 25b3f65 commit 6dd9806

File tree

5 files changed

+73
-33
lines changed

5 files changed

+73
-33
lines changed

Diff for: CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 3.3.1
2+
3+
This is a patch release that:
4+
5+
- Fixes an issue in dart2js where object literal constructors in interop
6+
extension types would fail to compile without an `@JS` annotation on the
7+
library (issue [#55057][]).
8+
9+
[#55057]: https://github.com/dart-lang/sdk/issues/55057
10+
111
## 3.3.0 - 2024-02-15
212

313
### Language

Diff for: pkg/compiler/lib/src/kernel/native_basic_data.dart

+13-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,19 @@ class KernelAnnotationProcessor {
8484
}
8585
} else {
8686
FunctionEntity function = member as FunctionEntity;
87-
if (function.isExternal && isExplicitlyJsLibrary) {
87+
// We need this explicit check as object literal constructors in
88+
// extension types do not need an `@JS()` annotation on them, their
89+
// extension type, or their library. JS interop checks assert that the
90+
// only extension type interop member that has named parameters is an
91+
// object literal constructor.
92+
// TODO(54968): We should handle the lowering for object literal
93+
// constructors in the interop transformer somehow instead and avoid
94+
// assuming all such members are object literal constructors or
95+
// otherwise paying the cost to verify by indexing extension types.
96+
bool isObjectLiteralConstructor = (memberNode.isExtensionTypeMember &&
97+
memberNode.function?.namedParameters.isNotEmpty == true);
98+
if (function.isExternal &&
99+
(isExplicitlyJsLibrary || isObjectLiteralConstructor)) {
88100
// External members of explicit js-interop library are implicitly
89101
// js-interop members.
90102
memberName ??= function.name;

Diff for: pkg/compiler/lib/src/ssa/builder.dart

+7-5
Original file line numberDiff line numberDiff line change
@@ -4168,18 +4168,20 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
41684168
List.from(_visitPositionalArguments(arguments));
41694169

41704170
if (target.namedParameters.isNotEmpty) {
4171-
// Only anonymous factory or inline class literal constructors involving
4171+
// Only anonymous factory or extension type literal constructors involving
41724172
// JS interop are allowed to have named parameters. Otherwise, throw an
41734173
// error.
41744174
final member = target.parent as ir.Member;
41754175
final function = _elementMap.getMember(member) as FunctionEntity;
41764176
bool isAnonymousFactory = function is ConstructorEntity &&
41774177
function.isFactoryConstructor &&
41784178
_nativeData.isAnonymousJsInteropClass(function.enclosingClass);
4179-
// JS interop checks assert that the only inline class interop member that
4180-
// has named parameters is an object literal constructor. We could do a
4181-
// more robust check by visiting all inline classes and recording
4182-
// descriptors, but that's expensive.
4179+
// JS interop checks assert that the only extension type interop member
4180+
// that has named parameters is an object literal constructor.
4181+
// TODO(54968): We should handle the lowering for object literal
4182+
// constructors in the interop transformer somehow instead and avoid
4183+
// assuming all such members are object literal constructors or
4184+
// otherwise paying the cost to verify by indexing extension types.
41834185
bool isObjectLiteralConstructor = member.isExtensionTypeMember;
41844186
if (isAnonymousFactory || isObjectLiteralConstructor) {
41854187
// TODO(sra): Have a "CompiledArguments" structure to just update with

Diff for: tests/lib/js/static_interop_test/extension_type/object_literal_constructor_test.dart

+20-27
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,38 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// SharedOptions=--enable-experiment=inline-class
6-
7-
@JS()
85
library object_literal_constructor_test;
96

107
import 'dart:js_interop';
11-
import 'dart:js_util';
8+
import 'dart:js_interop_unsafe';
129

1310
import 'package:expect/minitest.dart';
1411

15-
@JS()
16-
extension type Literal._(JSObject _) implements Object {
12+
extension type Literal._(JSObject _) implements JSObject {
1713
external Literal({double? a, String b, bool? c});
1814
external factory Literal.fact({double? a, String b, bool? c});
1915
}
2016

21-
@JS('Object.keys')
22-
external JSArray objectKeys(Literal literal);
23-
24-
void main() {
25-
// Test that the properties we assumed to exist in `literal` actually exist
26-
// and that their values are as expected. Note that we don't check the order
27-
// of the keys in the literal. This is not guaranteed to be the same across
28-
// different backends.
29-
void testProperties(Literal literal, {double? a, String? b, bool? c}) {
30-
if (a != null) {
31-
expect(hasProperty(literal, 'a'), true);
32-
expect(getProperty<double>(literal, 'a'), a);
33-
}
34-
if (b != null) {
35-
expect(hasProperty(literal, 'b'), true);
36-
expect(getProperty<String>(literal, 'b'), b);
37-
}
38-
if (c != null) {
39-
expect(hasProperty(literal, 'c'), true);
40-
expect(getProperty<bool>(literal, 'c'), c);
41-
}
17+
// Test that the properties we assumed to exist in `literal` actually exist and
18+
// that their values are as expected. Note that we don't check the order of the
19+
// keys in the literal. This is not guaranteed to be the same across different
20+
// backends.
21+
void testProperties(JSObject literal, {double? a, String? b, bool? c}) {
22+
if (a != null) {
23+
expect(literal.has('a'), true);
24+
expect((literal['a'] as JSNumber).toDartDouble, a);
25+
}
26+
if (b != null) {
27+
expect(literal.has('b'), true);
28+
expect((literal['b'] as JSString).toDart, b);
4229
}
30+
if (c != null) {
31+
expect(literal.has('c'), true);
32+
expect((literal['c'] as JSBoolean).toDart, c);
33+
}
34+
}
4335

36+
void main() {
4437
testProperties(Literal());
4538
testProperties(Literal.fact(a: 0.0), a: 0.0);
4639
testProperties(Literal(b: ''), b: '');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2024, 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+
// Validate that the library adding an annotation doesn't change the backend's
6+
// lowerings.
7+
8+
@JS()
9+
library object_literal_constructor_with_library_annotation_test;
10+
11+
import 'dart:js_interop';
12+
13+
import 'object_literal_constructor_test.dart' show testProperties;
14+
15+
extension type Literal._(JSObject _) implements JSObject {
16+
external Literal({double? a});
17+
external factory Literal.fact({double? a});
18+
}
19+
20+
void main() {
21+
testProperties(Literal());
22+
testProperties(Literal.fact(a: 0.0), a: 0.0);
23+
}

0 commit comments

Comments
 (0)