Skip to content

Commit

Permalink
Version 3.3.0-261.0.dev
Browse files Browse the repository at this point in the history
Merge 40dfd1b into dev
  • Loading branch information
Dart CI committed Dec 26, 2023
2 parents cf2d965 + 40dfd1b commit add161c
Show file tree
Hide file tree
Showing 25 changed files with 833 additions and 281 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@
types whose type parameter is a subtype of `JSAny?`. Conversions to and from
these types are changed to account for the type parameters of the Dart or JS
type, respectively.
- **Breaking Change in names of extensions**: Some `dart:js_interop` extension
members are moved to different extensions on the same type or a supertype to
better organize the API surface. See `JSAnyUtilityExtension` and
`JSAnyOperatorExtension` for the new extensions. This shouldn't make a
difference unless the extension names were explicitly used.

[#52687]: https://github.com/dart-lang/sdk/issues/52687

Expand Down
5 changes: 3 additions & 2 deletions pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9016,8 +9016,9 @@ const Code<Null> codeJsInteropOperatorsNotSupported =
const MessageCode messageJsInteropOperatorsNotSupported = const MessageCode(
"JsInteropOperatorsNotSupported",
problemMessage:
r"""JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop.""",
correctionMessage: r"""Try replacing this with a normal method.""");
r"""JS interop types do not support overloading external operator methods, with the exception of '[]' and '[]=' using static interop.""",
correctionMessage:
r"""Try making this class a static interop type instead.""");

// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeJsInteropStaticInteropGenerativeConstructor =
Expand Down
10 changes: 3 additions & 7 deletions pkg/_js_interop_checks/lib/js_interop_checks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ class JsInteropChecks extends RecursiveVisitor {
void report(Message message) => _reporter.report(
message, node.fileOffset, node.name.text.length, node.fileUri);

// TODO(joshualitt): Add a check that only supported operators are allowed
// in external extension members and extension types.
_checkInstanceMemberJSAnnotation(node);
if (_classHasJSAnnotation &&
!node.isExternal &&
Expand All @@ -319,7 +317,7 @@ class JsInteropChecks extends RecursiveVisitor {
if (!_isJSInteropMember(node)) {
_checkDisallowedExternal(node);
} else {
_checkJsInteropMemberNotOperator(node);
_checkJsInteropOperator(node);

// Check JS Interop positional and named parameters. Literal constructors
// can only have named parameters, and every other interop member can only
Expand Down Expand Up @@ -735,10 +733,8 @@ class JsInteropChecks extends RecursiveVisitor {
}

/// Given JS interop member [node], checks that it is not an operator that is
/// disallowed.
///
/// Also checks that no renaming is done on interop operators.
void _checkJsInteropMemberNotOperator(Procedure node) {
/// disallowed, on a non-static interop type, or renamed.
void _checkJsInteropOperator(Procedure node) {
var isInvalidOperator = false;
var operatorHasRenaming = false;
if ((node.isExtensionTypeMember &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ class ExportChecker {

/// Determine if [cls] is exportable, and if so, compute the export members.
///
///
/// Check the following:
/// - If the class has a `@JSExport` annotation, the value should be empty.
/// - If the class has the annotation, it should have at least one exportable
Expand Down
29 changes: 19 additions & 10 deletions pkg/_js_interop_checks/lib/src/transformations/export_creator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'static_interop_mock_validator.dart';
class ExportCreator extends Transformer {
final Procedure _callMethodVarArgs;
final Procedure _createDartExport;
final Procedure _createJSInteropWrapper;
final Procedure _createStaticInteropMock;
final JsInteropDiagnosticReporter _diagnosticReporter;
final ExportChecker _exportChecker;
Expand All @@ -42,6 +43,8 @@ class ExportCreator extends Transformer {
'JSObjectUnsafeUtilExtension|callMethodVarArgs'),
_createDartExport = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createDartExport'),
_createJSInteropWrapper = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop', 'createJSInteropWrapper'),
_createStaticInteropMock = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createStaticInteropMock'),
_functionToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
Expand All @@ -63,13 +66,19 @@ class ExportCreator extends Transformer {

@override
TreeNode visitStaticInvocation(StaticInvocation node) {
if (node.target == _createDartExport) {
final target = node.target;
if (target == _createDartExport || target == _createJSInteropWrapper) {
final typeArguments = node.arguments.types;
assert(typeArguments.length == 1);
if (_verifyExportable(node, typeArguments[0])) {
return _createExport(node, typeArguments[0] as InterfaceType);
final interface = typeArguments[0] as InterfaceType;
if (target == _createJSInteropWrapper) {
return _createExport(node, interface,
ExtensionType(_jsObject, Nullability.nonNullable));
}
return _createExport(node, interface);
}
} else if (node.target == _createStaticInteropMock) {
} else if (target == _createStaticInteropMock) {
final typeArguments = node.arguments.types;
assert(typeArguments.length == 2);
final staticInteropType = typeArguments[0];
Expand Down Expand Up @@ -98,8 +107,8 @@ class ExportCreator extends Transformer {
return node;
}

/// Validate that the [dartType] provided via `createDartExport` can be
/// exported safely.
/// Validate that the [dartType] provided via `createDartExport` or
/// `createJSInteropWrapper` can be exported safely.
///
/// Checks that:
/// - Type argument is a valid Dart interface type.
Expand Down Expand Up @@ -158,11 +167,11 @@ class ExportCreator extends Transformer {
/// Create the object literal using the export map that was computed from the
/// interface in [dartType].
///
/// [node] is either a call to `createStaticInteropMock` or
/// `createDartExport`. [dartType] is assumed to be a valid exportable class.
/// [returnType] is the type that the object literal will be casted to.
/// [proto] is an optional prototype object that users can pass to instantiate
/// the object literal.
/// [node] is either a call to `createStaticInteropMock`, `createDartExport`,
/// or `createJSInteropWrapper`. [dartType] is assumed to be a valid
/// exportable class. [returnType] is the type that the object literal will be
/// casted to. [proto] is an optional prototype object that users can pass to
/// instantiate the object literal.
///
/// The export map is already validated, so this method simply iterates over
/// it and either assigns a method for a given property name, or assigns a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,25 +274,19 @@ class JsUtilOptimizer extends Transformer {
[Expression? maybeReceiver]) {
final target =
shouldTrustType ? _getPropertyTrustTypeTarget : _getPropertyTarget;
final isOperator = _extensionIndex.isOperator(node);
final isInstanceInteropMember =
_extensionIndex.isInstanceInteropMember(node);
final name = _getMemberJSName(node);
return (Arguments arguments, Expression invocation) {
// Parameter `this` only exists for extension and extension type instance
// members. Operators take a `this` and an index.
// members.
final positionalArgs = arguments.positional;
assert(positionalArgs.length ==
(isOperator
? 2
: isInstanceInteropMember
? 1
: 0));
assert(positionalArgs.length == (isInstanceInteropMember ? 1 : 0));
// We clone the receiver as each invocation needs a fresh node.
final receiver = maybeReceiver == null
? positionalArgs.first
: _cloner.clone(maybeReceiver);
final property = isOperator ? positionalArgs[1] : StringLiteral(name);
final property = StringLiteral(name);
return StaticInvocation(
target,
Arguments([receiver, property],
Expand All @@ -311,24 +305,18 @@ class JsUtilOptimizer extends Transformer {
/// positional argument as the receiver for `js_util.setProperty`.
_InvocationBuilder _getExternalSetterInvocationBuilder(Procedure node,
[Expression? maybeReceiver]) {
final isOperator = _extensionIndex.isOperator(node);
final isInstanceInteropMember =
_extensionIndex.isInstanceInteropMember(node);
final name = _getMemberJSName(node);
return (Arguments arguments, Expression invocation) {
// Parameter `this` only exists for extension and extension type instance
// members. Operators take a `this`, an index, and a value.
// members.
final positionalArgs = arguments.positional;
assert(positionalArgs.length ==
(isOperator
? 3
: isInstanceInteropMember
? 2
: 1));
assert(positionalArgs.length == (isInstanceInteropMember ? 2 : 1));
final receiver = maybeReceiver == null
? positionalArgs.first
: _cloner.clone(maybeReceiver);
final property = isOperator ? positionalArgs[1] : StringLiteral(name);
final property = StringLiteral(name);
final value = positionalArgs.last;
return StaticInvocation(
_setPropertyTarget,
Expand Down Expand Up @@ -389,14 +377,32 @@ class JsUtilOptimizer extends Transformer {
final operator =
_extensionIndex.getExtensionTypeDescriptor(node)?.name.text ??
_extensionIndex.getExtensionDescriptor(node)?.name.text;

// TODO(srujzs): Support more operators for overloading using some
// combination of Dart-defineable operators and @JS renaming for the ones
// that are not renameable.
late Procedure target;
switch (operator) {
case '[]':
return _getExternalGetterInvocationBuilder(node, shouldTrustType);
target =
shouldTrustType ? _getPropertyTrustTypeTarget : _getPropertyTarget;
break;
case '[]=':
return _getExternalSetterInvocationBuilder(node);
target = _setPropertyTarget;
break;
default:
throw 'External operator $operator is unsupported for static interop.';
throw UnimplementedError(
'External operator $operator is unsupported for static interop. ');
}

return (Arguments arguments, Expression invocation) {
return StaticInvocation(
target,
Arguments(arguments.positional,
types: [invocation.getStaticType(_staticTypeContext)]))
..fileOffset = invocation.fileOffset
..parent = invocation.parent;
};
}

/// Returns a new [_InvocationBuilder] for the given [node] external
Expand Down
7 changes: 7 additions & 0 deletions pkg/analysis_server/lib/src/computer/computer_highlights.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,13 @@ class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {

@override
void visitNamedType(NamedType node) {
if (node.importPrefix case final importPrefix?) {
computer._addRegion_token(
importPrefix.name,
HighlightRegionType.IMPORT_PREFIX,
);
}

var type = node.type;
if (type != null) {
var isDynamic = type is DynamicType && node.name2.lexeme == 'dynamic';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,28 @@ void f() {
assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma.max');
}

Future<void> test_IMPORT_PREFIX_methodInvocation() async {
addTestFile('''
import 'dart:math' as ma;
void f() {
ma.max(1, 2);
}
''');
await prepareHighlights();
assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma;');
assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'ma.max');
}

Future<void> test_IMPORT_PREFIX_namedType() async {
addTestFile('''
import 'dart:math' as math;
void f(math.Random r) {}
''');
await prepareHighlights();
assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'math;');
assertHasRegion(HighlightRegionType.IMPORT_PREFIX, 'math.Random');
}

Future<void> test_INSTANCE_FIELD() async {
addTestFile('''
class A {
Expand Down
9 changes: 8 additions & 1 deletion pkg/analyzer/lib/src/dart/element/type_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ class ExtensionTypeErasure extends ReplacementVisitor {
@override
DartType? visitInterfaceType(covariant InterfaceTypeImpl type) {
if (type.representationType case final representationType?) {
return representationType.accept(this) ?? representationType;
final erased = representationType.accept(this) ?? representationType;
erased as TypeImpl;
// If the extension type is nullable, apply it to the erased.
if (type.nullabilitySuffix == NullabilitySuffix.question) {
return erased.withNullability(NullabilitySuffix.question);
}
// Use the erased as is, still might be nullable.
return erased;
}

return super.visitInterfaceType(type);
Expand Down
19 changes: 19 additions & 0 deletions pkg/analyzer/test/src/dart/constant/evaluation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,25 @@ const v = int;
_assertHasPrimitiveEqualityTrue('v');
}

test_identical_extensionType_nullable() async {
await assertNoErrorsInCode('''
extension type E(int it) {}
class A {
final E? f;
const A() : f = null;
}
const v = A();
''');
final result = _topLevelVar('v');
assertDartObjectText(result, r'''
A
f: Null null
variable: self::@variable::v
''');
}

test_identical_extensionType_types_recursive() async {
await assertNoErrorsInCode('''
const c = identical(ExList<ExInt>, List<int>);
Expand Down
16 changes: 10 additions & 6 deletions pkg/dart2wasm/lib/js/interop_specializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,16 @@ class _OperatorSpecializer extends _ProcedureSpecializer {

@override
String bodyString(String object, List<String> callArguments) {
if (jsString == '[]') {
return '$object[${callArguments[0]}]';
} else if (jsString == '[]=') {
return '$object[${callArguments[0]}] = ${callArguments[1]}';
} else {
throw 'Unsupported operator: $jsString';
// TODO(srujzs): Switch to switch-case expression once we update pubspec.
switch (jsString) {
case '[]':
return '$object[${callArguments[0]}]';
case '[]=':
return '$object[${callArguments[0]}] = ${callArguments[1]}';
default:
throw UnimplementedError(
'External operator $jsString is unsupported for static interop. '
'Please file a request in the SDK if you want it to be supported.');
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/front_end/messages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5904,8 +5904,8 @@ JsInteropOperatorCannotBeRenamed:
correctionMessage: "Remove the annotation or remove the value inside the annotation."

JsInteropOperatorsNotSupported:
problemMessage: "JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop."
correctionMessage: "Try replacing this with a normal method."
problemMessage: "JS interop types do not support overloading external operator methods, with the exception of '[]' and '[]=' using static interop."
correctionMessage: "Try making this class a static interop type instead."

JsInteropNonStaticWithStaticInteropSupertype:
problemMessage: "Class '#name' does not have an `@staticInterop` annotation, but has supertype '#name2', which does."
Expand Down
Loading

0 comments on commit add161c

Please sign in to comment.