Skip to content

Commit e9678f7

Browse files
srujzsCommit Queue
authored and
Commit Queue
committed
[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. Change-Id: Iee99375439057844485aa3f5cd88f85f5d03ae06 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/349840 Reviewed-by: Stephen Adams <[email protected]> Commit-Queue: Srujan Gaddam <[email protected]>
1 parent 9fe6571 commit e9678f7

File tree

4 files changed

+63
-33
lines changed

4 files changed

+63
-33
lines changed

pkg/compiler/lib/src/kernel/native_basic_data.dart

Lines changed: 13 additions & 1 deletion
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;

pkg/compiler/lib/src/ssa/builder.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4151,18 +4151,20 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
41514151
List.from(_visitPositionalArguments(arguments));
41524152

41534153
if (target.namedParameters.isNotEmpty) {
4154-
// Only anonymous factory or inline class literal constructors involving
4154+
// Only anonymous factory or extension type literal constructors involving
41554155
// JS interop are allowed to have named parameters. Otherwise, throw an
41564156
// error.
41574157
final member = target.parent as ir.Member;
41584158
final function = _elementMap.getMember(member) as FunctionEntity;
41594159
bool isAnonymousFactory = function is ConstructorEntity &&
41604160
function.isFactoryConstructor &&
41614161
_nativeData.isAnonymousJsInteropClass(function.enclosingClass);
4162-
// JS interop checks assert that the only inline class interop member that
4163-
// has named parameters is an object literal constructor. We could do a
4164-
// more robust check by visiting all inline classes and recording
4165-
// descriptors, but that's expensive.
4162+
// JS interop checks assert that the only extension type interop member
4163+
// that has named parameters is an object literal constructor.
4164+
// TODO(54968): We should handle the lowering for object literal
4165+
// constructors in the interop transformer somehow instead and avoid
4166+
// assuming all such members are object literal constructors or
4167+
// otherwise paying the cost to verify by indexing extension types.
41664168
bool isObjectLiteralConstructor = member.isExtensionTypeMember;
41674169
if (isAnonymousFactory || isObjectLiteralConstructor) {
41684170
// TODO(sra): Have a "CompiledArguments" structure to just update with

tests/lib/js/static_interop_test/extension_type/object_literal_constructor_test.dart

Lines changed: 20 additions & 27 deletions
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)