Version 2.17.0-149.0.dev
Merge commit 'e6ff207cf82aedbfeaa4e29df7ede0d1a55cbebd' into 'dev'
diff --git a/DEPS b/DEPS
index 2c99002..7e3aaea 100644
--- a/DEPS
+++ b/DEPS
@@ -93,7 +93,7 @@
"collection_rev": "a4c941ab94044d118b2086a3f261c30377604127",
"convert_rev": "e063fdca4bebffecbb5e6aa5525995120982d9ce",
"crypto_rev": "b5024e4de2b1c474dd558bef593ddbf0bfade152",
- "csslib_rev": "f746368a0a53cf8f68fd71b218239034e88841d5",
+ "csslib_rev": "518761b166974537f334dbf264e7f56cb157a96a",
# Note: Updates to dart_style have to be coordinated with the infrastructure
# team so that the internal formatter `tools/sdks/dart-sdk/bin/dart format`
@@ -109,7 +109,7 @@
# For more details, see https://github.com/dart-lang/sdk/issues/30164
"dart_style_rev": "6f894c0ca33686122be9085f06e5b9bf6ad55262",
- "dartdoc_rev" : "b3927dd89d6ff9c78dc88ab2901e63b6a3bf29b7",
+ "dartdoc_rev" : "a39f378f8100b907e6285ac825967d764fd664ad",
"devtools_rev" : "b9f2039239cc72ac8b26f8a5fe46123f34d53ce1",
"ffi_rev": "4dd32429880a57b64edaf54c9d5af8a9fa9a4ffb",
"fixnum_rev": "848341f061359ef7ddc0cad472c2ecbb036b28ac",
diff --git a/pkg/analysis_server/lib/src/computer/computer_overrides.dart b/pkg/analysis_server/lib/src/computer/computer_overrides.dart
index 92f68d0..41e8088 100644
--- a/pkg/analysis_server/lib/src/computer/computer_overrides.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_overrides.dart
@@ -27,23 +27,9 @@
List<proto.Override> compute() {
for (var unitMember in _unit.declarations) {
if (unitMember is ClassOrMixinDeclaration) {
- for (var classMember in unitMember.members) {
- if (classMember is MethodDeclaration) {
- if (classMember.isStatic) {
- continue;
- }
- _addOverride(classMember.name);
- }
- if (classMember is FieldDeclaration) {
- if (classMember.isStatic) {
- continue;
- }
- List<VariableDeclaration> fields = classMember.fields.variables;
- for (var field in fields) {
- _addOverride(field.name);
- }
- }
- }
+ _classMembers(unitMember.members);
+ } else if (unitMember is EnumDeclaration) {
+ _classMembers(unitMember.members);
}
}
return _overrides;
@@ -73,6 +59,26 @@
}
}
}
+
+ void _classMembers(List<ClassMember> members) {
+ for (var classMember in members) {
+ if (classMember is MethodDeclaration) {
+ if (classMember.isStatic) {
+ continue;
+ }
+ _addOverride(classMember.name);
+ }
+ if (classMember is FieldDeclaration) {
+ if (classMember.isStatic) {
+ continue;
+ }
+ List<VariableDeclaration> fields = classMember.fields.variables;
+ for (var field in fields) {
+ _addOverride(field.name);
+ }
+ }
+ }
+ }
}
/// The container with elements that a class member overrides.
@@ -92,26 +98,6 @@
}
class _OverriddenElementsFinder {
- static const List<ElementKind> FIELD_KINDS = <ElementKind>[
- ElementKind.FIELD,
- ElementKind.GETTER,
- ElementKind.SETTER
- ];
-
- static const List<ElementKind> GETTER_KINDS = <ElementKind>[
- ElementKind.FIELD,
- ElementKind.GETTER
- ];
-
- static const List<ElementKind> METHOD_KINDS = <ElementKind>[
- ElementKind.METHOD
- ];
-
- static const List<ElementKind> SETTER_KINDS = <ElementKind>[
- ElementKind.FIELD,
- ElementKind.SETTER
- ];
-
Element _seed;
LibraryElement _library;
ClassElement _class;
@@ -127,12 +113,19 @@
var library = class_.library;
var name = seed.displayName;
List<ElementKind> kinds;
- if (seed is MethodElement) {
- kinds = METHOD_KINDS;
+ if (seed is FieldElement) {
+ kinds = [
+ ElementKind.GETTER,
+ if (!seed.isFinal) ElementKind.SETTER,
+ ];
+ } else if (seed is MethodElement) {
+ kinds = const [ElementKind.METHOD];
} else if (seed is PropertyAccessorElement) {
- kinds = seed.isGetter ? GETTER_KINDS : SETTER_KINDS;
+ kinds = seed.isGetter
+ ? const [ElementKind.GETTER]
+ : const [ElementKind.SETTER];
} else {
- kinds = FIELD_KINDS;
+ kinds = const [];
}
return _OverriddenElementsFinder._(seed, library, class_, name, kinds);
}
diff --git a/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart b/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
index 4e42510..01507f9 100644
--- a/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
+++ b/pkg/analysis_server/lib/src/domains/analysis/implemented_dart.dart
@@ -18,10 +18,13 @@
ImplementedComputer(this.searchEngine, this.unitElement);
Future<void> compute() async {
- for (var element in unitElement.mixins) {
+ for (var element in unitElement.classes) {
await _computeForClassElement(element);
}
- for (var element in unitElement.classes) {
+ for (var element in unitElement.enums) {
+ await _computeForClassElement(element);
+ }
+ for (var element in unitElement.mixins) {
await _computeForClassElement(element);
}
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_leading_underscore.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_leading_underscore.dart
new file mode 100644
index 0000000..75e7dd0a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_leading_underscore.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class RemoveLeadingUnderscore extends CorrectionProducer {
+ @override
+ bool get canBeAppliedInBulk => true;
+
+ @override
+ bool get canBeAppliedToFile => true;
+
+ @override
+ FixKind get fixKind => DartFixKind.REMOVE_LEADING_UNDERSCORE;
+
+ @override
+ FixKind get multiFixKind => DartFixKind.REMOVE_LEADING_UNDERSCORE_MULTI;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ var identifier = node;
+ if (identifier is! SimpleIdentifier) {
+ return;
+ }
+
+ var name = identifier.name;
+ if (name.length < 2) {
+ return;
+ }
+
+ var newName = name.substring(1);
+
+ // Find references to the identifier.
+ List<SimpleIdentifier>? references;
+ var element = identifier.staticElement;
+ if (element is LocalVariableElement) {
+ var root = node.thisOrAncestorOfType<Block>();
+ if (root != null) {
+ references = findLocalElementReferences(root, element);
+ }
+ } else if (element is ParameterElement) {
+ if (!element.isNamed) {
+ print(node.parent.runtimeType);
+ print(node.parent?.parent.runtimeType);
+ print(node.parent?.parent?.parent.runtimeType);
+ var root = node
+ .thisOrAncestorMatching((node) =>
+ node.parent is FunctionDeclaration ||
+ node.parent is MethodDeclaration)
+ ?.parent;
+ if (root != null) {
+ references = findLocalElementReferences(root, element);
+ }
+ }
+ }
+ if (references == null) {
+ return;
+ }
+
+ // Compute the change.
+ var references_final = references;
+ await builder.addDartFileEdit(file, (builder) {
+ for (var reference in references_final) {
+ builder.addSimpleReplacement(range.node(reference), newName);
+ }
+ });
+ }
+
+ /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+ static RemoveLeadingUnderscore newInstance() => RemoveLeadingUnderscore();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 874bbdb..819a2a7 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -951,6 +951,16 @@
DartFixKindPriority.IN_FILE,
'Remove unnecessary interpolation braces everywhere in file',
);
+ static const REMOVE_LEADING_UNDERSCORE = FixKind(
+ 'dart.fix.remove.leadingUnderscore',
+ DartFixKindPriority.DEFAULT,
+ 'Remove leading underscore',
+ );
+ static const REMOVE_LEADING_UNDERSCORE_MULTI = FixKind(
+ 'dart.fix.remove.leadingUnderscore.multi',
+ DartFixKindPriority.IN_FILE,
+ 'Remove leading underscores in file',
+ );
static const REMOVE_METHOD_DECLARATION = FixKind(
'dart.fix.remove.methodDeclaration',
DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 9258b2f..901695f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -118,6 +118,7 @@
import 'package:analysis_server/src/services/correction/dart/remove_if_null_operator.dart';
import 'package:analysis_server/src/services/correction/dart/remove_initializer.dart';
import 'package:analysis_server/src/services/correction/dart/remove_interpolation_braces.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_leading_underscore.dart';
import 'package:analysis_server/src/services/correction/dart/remove_method_declaration.dart';
import 'package:analysis_server/src/services/correction/dart/remove_name_from_combinator.dart';
import 'package:analysis_server/src/services/correction/dart/remove_non_null_assertion.dart';
@@ -452,6 +453,9 @@
LintNames.no_duplicate_case_values: [
RemoveDuplicateCase.newInstance,
],
+ LintNames.no_leading_underscores_for_local_identifiers: [
+ RemoveLeadingUnderscore.newInstance,
+ ],
LintNames.non_constant_identifier_names: [
RenameToCamelCase.newInstance,
],
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 123600d..986a5a4 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -59,6 +59,8 @@
static const String leading_newlines_in_multiline_strings =
'leading_newlines_in_multiline_strings';
static const String no_duplicate_case_values = 'no_duplicate_case_values';
+ static const String no_leading_underscores_for_local_identifiers =
+ 'no_leading_underscores_for_local_identifiers';
static const String non_constant_identifier_names =
'non_constant_identifier_names';
static const String null_closures = 'null_closures';
diff --git a/pkg/analysis_server/test/analysis/notification_implemented_test.dart b/pkg/analysis_server/test/analysis/notification_implemented_test.dart
index c0deb4e..a9db25d 100644
--- a/pkg/analysis_server/test/analysis/notification_implemented_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_implemented_test.dart
@@ -150,7 +150,7 @@
assertHasImplementedClass('A {');
}
- Future<void> test_class_implemented() async {
+ Future<void> test_class_implementedBy_class() async {
addTestFile('''
class A {}
class B implements A {}
@@ -159,7 +159,67 @@
assertHasImplementedClass('A {');
}
- Future<void> test_class_inMixin() async {
+ Future<void> test_class_implementedBy_enum() async {
+ addTestFile('''
+class A {}
+
+enum E implements A {
+ v
+}
+''');
+ await prepareImplementedElements();
+ assertHasImplementedClass('A {');
+ }
+
+ Future<void> test_class_implementedBy_enum_getterByGetter() async {
+ addTestFile('''
+class A {
+ int get foo => 0; // A
+}
+
+enum E implements A {
+ v;
+ int get foo => 0; // E
+}
+''');
+ await prepareImplementedElements();
+ assertHasImplementedMember('foo => 0; // A');
+ assertNoImplementedMember('foo => 0; // E');
+ }
+
+ Future<void> test_class_implementedBy_enum_methodByMethod() async {
+ addTestFile('''
+class A {
+ void foo() {} // A
+}
+
+enum E implements A {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareImplementedElements();
+ assertHasImplementedMember('foo() {} // A');
+ assertNoImplementedMember('foo() {} // E');
+ }
+
+ Future<void> test_class_implementedBy_enum_setterBySetter() async {
+ addTestFile('''
+class A {
+ set foo(int _) {} // A
+}
+
+enum E implements A {
+ v;
+ set foo(int _) {} // E
+}
+''');
+ await prepareImplementedElements();
+ assertHasImplementedMember('foo(int _) {} // A');
+ assertNoImplementedMember('foo(int _) {} // E');
+ }
+
+ Future<void> test_class_implementedBy_mixin() async {
addTestFile('''
class A {} // ref
class B {} // ref
@@ -174,7 +234,7 @@
assertHasImplementedClass('D {} // ref');
}
- Future<void> test_class_mixed() async {
+ Future<void> test_class_mixedBy_class() async {
addTestFile('''
class A {}
class B = Object with A;
@@ -183,6 +243,33 @@
assertHasImplementedClass('A {');
}
+ Future<void> test_class_mixedBy_enum() async {
+ addTestFile('''
+mixin M {}
+enum E with M {
+ v
+}
+''');
+ await prepareImplementedElements();
+ assertHasImplementedClass('M {}');
+ }
+
+ Future<void> test_class_mixedBy_enum_methodByMethod() async {
+ addTestFile('''
+class M {
+ void foo() {} // M
+}
+
+enum E with M {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareImplementedElements();
+ assertHasImplementedMember('foo() {} // M');
+ assertNoImplementedMember('foo() {} // E');
+ }
+
Future<void> test_field_withField() async {
addTestFile('''
class A {
diff --git a/pkg/analysis_server/test/analysis/notification_overrides_test.dart b/pkg/analysis_server/test/analysis/notification_overrides_test.dart
index bc48633..3ce1928 100644
--- a/pkg/analysis_server/test/analysis/notification_overrides_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_overrides_test.dart
@@ -142,7 +142,7 @@
assertHasInterfaceMember('m() {} // in A');
}
- Future<void> test_BAD_fieldByMethod() async {
+ Future<void> test_class_BAD_fieldByMethod() async {
addTestFile('''
class A {
int fff; // in A
@@ -155,7 +155,7 @@
assertNoOverride('fff() {} // in B');
}
- Future<void> test_BAD_getterByMethod() async {
+ Future<void> test_class_BAD_getterByMethod() async {
addTestFile('''
class A {
get fff => null;
@@ -168,7 +168,7 @@
assertNoOverride('fff() {}');
}
- Future<void> test_BAD_getterBySetter() async {
+ Future<void> test_class_BAD_getterBySetter() async {
addTestFile('''
class A {
get fff => null;
@@ -181,7 +181,7 @@
assertNoOverride('fff(x) {}');
}
- Future<void> test_BAD_methodByField() async {
+ Future<void> test_class_BAD_methodByField() async {
addTestFile('''
class A {
fff() {} // in A
@@ -194,7 +194,7 @@
assertNoOverride('fff; // in B');
}
- Future<void> test_BAD_methodByGetter() async {
+ Future<void> test_class_BAD_methodByGetter() async {
addTestFile('''
class A {
fff() {}
@@ -207,7 +207,7 @@
assertNoOverride('fff => null');
}
- Future<void> test_BAD_methodBySetter() async {
+ Future<void> test_class_BAD_methodBySetter() async {
addTestFile('''
class A {
fff(x) {} // A
@@ -220,7 +220,7 @@
assertNoOverride('fff(x) {} // B');
}
- Future<void> test_BAD_privateByPrivate_inDifferentLib() async {
+ Future<void> test_class_BAD_privateByPrivate_inDifferentLib() async {
newFile(join(testFolder, 'lib.dart'), content: r'''
class A {
void _m() {}
@@ -236,7 +236,7 @@
assertNoOverride('_m() {} // in B');
}
- Future<void> test_BAD_setterByGetter() async {
+ Future<void> test_class_BAD_setterByGetter() async {
addTestFile('''
class A {
set fff(x) {}
@@ -249,7 +249,7 @@
assertNoOverride('fff => null;');
}
- Future<void> test_BAD_setterByMethod() async {
+ Future<void> test_class_BAD_setterByMethod() async {
addTestFile('''
class A {
set fff(x) {} // A
@@ -262,7 +262,7 @@
assertNoOverride('fff(x) {} // B');
}
- Future<void> test_definedInInterface_ofInterface() async {
+ Future<void> test_class_definedInInterface_ofInterface() async {
addTestFile('''
class A {
m() {} // in A
@@ -278,7 +278,7 @@
assertHasInterfaceMember('m() {} // in A');
}
- Future<void> test_definedInInterface_ofSuper() async {
+ Future<void> test_class_definedInInterface_ofSuper() async {
addTestFile('''
class A {
m() {} // in A
@@ -294,39 +294,7 @@
assertHasInterfaceMember('m() {} // in A');
}
- Future<void> test_inMixin_interface_method_direct_single() async {
- addTestFile('''
-class A {
- m() {} // in A
-}
-
-mixin M implements A {
- m() {} // in M
-}
-''');
- await prepareOverrides();
- assertHasOverride('m() {} // in M');
- assertNoSuperMember();
- assertHasInterfaceMember('m() {} // in A');
- }
-
- Future<void> test_inMixin_superclassConstraint_method_direct() async {
- addTestFile('''
-class A {
- m() {} // in A
-}
-
-mixin M on A {
- m() {} // in M
-}
-''');
- await prepareOverrides();
- assertHasOverride('m() {} // in M');
- assertHasSuperElement('m() {} // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_interface_method_direct_multiple() async {
+ Future<void> test_class_interface_method_direct_multiple() async {
addTestFile('''
class IA {
m() {} // in IA
@@ -345,7 +313,7 @@
assertHasInterfaceMember('m() {} // in IB');
}
- Future<void> test_interface_method_direct_single() async {
+ Future<void> test_class_interface_method_direct_single() async {
addTestFile('''
class A {
m() {} // in A
@@ -360,7 +328,7 @@
assertHasInterfaceMember('m() {} // in A');
}
- Future<void> test_interface_method_indirect_single() async {
+ Future<void> test_class_interface_method_indirect_single() async {
addTestFile('''
class A {
m() {} // in A
@@ -377,7 +345,7 @@
assertHasInterfaceMember('m() {} // in A');
}
- Future<void> test_interface_stopWhenFound() async {
+ Future<void> test_class_interface_stopWhenFound() async {
addTestFile('''
class A {
m() {} // in A
@@ -395,7 +363,7 @@
assertHasInterfaceMember('m() {} // in B');
}
- Future<void> test_mix_sameMethod() async {
+ Future<void> test_class_mix_sameMethod() async {
addTestFile('''
class A {
m() {} // in A
@@ -412,7 +380,7 @@
assertNoInterfaceMembers();
}
- Future<void> test_mix_sameMethod_Object_hashCode() async {
+ Future<void> test_class_mix_sameMethod_Object_hashCode() async {
addTestFile('''
class A {}
abstract class B {}
@@ -426,6 +394,536 @@
expect(overrideObject.interfaceMembers, isNull);
}
+ Future<void> test_class_staticMembers() async {
+ addTestFile('''
+class A {
+ static int F = 0;
+ static void M() {}
+ static int get G => 0;
+ static void set S(int v) {}
+}
+class B extends A {
+ static int F = 0;
+ static void M() {}
+ static int get G => 0;
+ static void set S(int v) {}
+}
+''');
+ await prepareOverrides();
+ expect(overridesList, isEmpty);
+ }
+
+ Future<void> test_class_super_fieldByField() async {
+ addTestFile('''
+class A {
+ int fff; // in A
+}
+class B extends A {
+ int fff; // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('fff; // in B');
+ assertHasSuperElement('fff; // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_fieldByGetter() async {
+ addTestFile('''
+class A {
+ int fff; // in A
+}
+class B extends A {
+ get fff => 0; // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('fff => 0; // in B');
+ assertHasSuperElement('fff; // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_fieldBySetter() async {
+ addTestFile('''
+class A {
+ int fff; // in A
+}
+class B extends A {
+ set fff(x) {} // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('fff(x) {} // in B');
+ assertHasSuperElement('fff; // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_getterByField() async {
+ addTestFile('''
+class A {
+ get fff => 0; // in A
+ set fff(x) {} // in A
+}
+class B extends A {
+ int fff; // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('fff; // in B');
+ assertHasSuperElement('fff => 0; // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_getterByGetter() async {
+ addTestFile('''
+class A {
+ get fff => 0; // in A
+}
+class B extends A {
+ get fff => 0; // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('fff => 0; // in B');
+ assertHasSuperElement('fff => 0; // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_method_direct() async {
+ addTestFile('''
+class A {
+ m() {} // in A
+}
+class B extends A {
+ m() {} // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('m() {} // in B');
+ assertHasSuperElement('m() {} // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_method_indirect() async {
+ addTestFile('''
+class A {
+ m() {} // in A
+}
+class B extends A {
+}
+class C extends B {
+ m() {} // in C
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('m() {} // in C');
+ assertHasSuperElement('m() {} // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_method_privateByPrivate() async {
+ addTestFile('''
+class A {
+ _m() {} // in A
+}
+class B extends A {
+ _m() {} // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('_m() {} // in B');
+ assertHasSuperElement('_m() {} // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_class_super_method_superTypeCycle() async {
+ addTestFile('''
+class A extends B {
+ m() {} // in A
+}
+class B extends A {
+ m() {} // in B
+}
+''');
+ await prepareOverrides();
+ // must finish
+ }
+
+ Future<void> test_class_super_setterBySetter() async {
+ addTestFile('''
+class A {
+ set fff(x) {} // in A
+}
+class B extends A {
+ set fff(x) {} // in B
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('fff(x) {} // in B');
+ assertHasSuperElement('fff(x) {} // in A');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_enum_interface_getterByGetter() async {
+ addTestFile('''
+class A {
+ int get foo => 0; // A
+}
+
+enum E implements A {
+ v;
+ int get foo => 0; // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo => 0; // E');
+ assertNoSuperMember();
+ assertHasInterfaceMember('foo => 0; // A');
+ }
+
+ Future<void> test_enum_interface_methodByMethod() async {
+ addTestFile('''
+class A {
+ void foo() {} // A
+}
+
+enum E implements A {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo() {} // E');
+ assertNoSuperMember();
+ assertHasInterfaceMember('foo() {} // A');
+ }
+
+ Future<void> test_enum_interface_methodByMethod2() async {
+ addTestFile('''
+class A {
+ void foo() {} // A
+}
+
+class B {
+ void foo() {} // B
+}
+
+enum E implements A, B {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo() {} // E');
+ assertNoSuperMember();
+ assertHasInterfaceMember('foo() {} // A');
+ assertHasInterfaceMember('foo() {} // B');
+ }
+
+ Future<void> test_enum_interface_methodByMethod_indirect() async {
+ addTestFile('''
+abstract class A {
+ void foo(); // A
+}
+
+abstract class B implements A {}
+
+enum E implements B {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo() {} // E');
+ assertNoSuperMember();
+ assertHasInterfaceMember('foo(); // A');
+ }
+
+ Future<void> test_enum_interface_setterBySetter() async {
+ addTestFile('''
+class A {
+ set foo(int _) {} // A
+}
+
+enum E implements A {
+ v;
+ set foo(int _) {} // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo(int _) {} // E');
+ assertNoSuperMember();
+ assertHasInterfaceMember('foo(int _) {} // A');
+ }
+
+ Future<void> test_enum_super_fieldByField() async {
+ addTestFile('''
+mixin M {
+ final int foo = 0; // M
+}
+
+enum E with M {
+ v;
+ final int foo = 0; // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo = 0; // E');
+ assertHasSuperElement('foo = 0; // M');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_enum_super_fieldByGetter() async {
+ addTestFile('''
+mixin M {
+ final int foo = 0; // M
+}
+
+enum E with M {
+ v;
+ int get foo => 0; // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo => 0; // E');
+ assertHasSuperElement('foo = 0; // M');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_enum_super_fieldByMethod() async {
+ addTestFile('''
+mixin M {
+ final int foo = 0; // M
+}
+
+enum E with M {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo() {} // E');
+ }
+
+ Future<void> test_enum_super_fieldBySetter() async {
+ addTestFile('''
+mixin M {
+ final int foo = 0; // M
+}
+
+enum E with M {
+ v;
+ set foo(int _) {} // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo(int _) {} // E');
+ }
+
+ Future<void> test_enum_super_getterByField() async {
+ addTestFile('''
+mixin M {
+ int get foo => 0; // M
+}
+
+enum E with M {
+ v;
+ final int foo = 0; // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo = 0; // E');
+ assertHasSuperElement('foo => 0; // M');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_enum_super_getterByGetter() async {
+ addTestFile('''
+mixin M {
+ int get foo => 0; // M
+}
+
+enum E with M {
+ v;
+ int get foo => 0; // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo => 0; // E');
+ assertHasSuperElement('foo => 0; // M');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_enum_super_getterByMethod() async {
+ addTestFile('''
+mixin M {
+ int get foo => 0; // M
+}
+
+enum E with M {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo() {} // E');
+ }
+
+ Future<void> test_enum_super_getterBySetter() async {
+ addTestFile('''
+mixin M {
+ int get foo => 0; // M
+}
+
+enum E with M {
+ v;
+ set foo(int _) {} // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo(int _) {} // E');
+ }
+
+ Future<void> test_enum_super_methodByField() async {
+ addTestFile('''
+mixin M {
+ void foo() {} // M
+}
+
+enum E with M {
+ v;
+ final int foo = 0; // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo = 0; // E');
+ }
+
+ Future<void> test_enum_super_methodByGetter() async {
+ addTestFile('''
+mixin M {
+ void foo() {} // M
+}
+
+enum E with M {
+ v;
+ int get foo => 0; // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo => 0; // E');
+ }
+
+ Future<void> test_enum_super_methodByMethod() async {
+ addTestFile('''
+mixin M {
+ void foo() {} // M
+}
+
+enum E with M {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo() {} // E');
+ assertHasSuperElement('foo() {} // M');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_enum_super_methodBySetter() async {
+ addTestFile('''
+mixin M {
+ void foo() {} // M
+}
+
+enum E with M {
+ v;
+ set foo(int _) {} // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo(int _) {} // E');
+ }
+
+ Future<void> test_enum_super_setterByField() async {
+ addTestFile('''
+mixin M {
+ set foo(int _) {} // M
+}
+
+enum E with M {
+ v;
+ final int foo = 0; // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo = 0; // E');
+ }
+
+ Future<void> test_enum_super_setterByGetter() async {
+ addTestFile('''
+mixin M {
+ set foo(int _) {} // M
+}
+
+enum E with M {
+ v;
+ int get foo => 0; // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo => 0; // E');
+ }
+
+ Future<void> test_enum_super_setterByMethod() async {
+ addTestFile('''
+mixin M {
+ set foo(int _) {} // M
+}
+
+enum E with M {
+ v;
+ void foo() {} // E
+}
+''');
+ await prepareOverrides();
+ assertNoOverride('foo() {} // E');
+ }
+
+ Future<void> test_enum_super_setterBySetter() async {
+ addTestFile('''
+mixin M {
+ set foo(int _) {} // M
+}
+
+enum E with M {
+ v;
+ set foo(int _) {} // E
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('foo(int _) {} // E');
+ assertHasSuperElement('foo(int _) {} // M');
+ assertNoInterfaceMembers();
+ }
+
+ Future<void> test_mixin_interface_method_direct_single() async {
+ addTestFile('''
+class A {
+ m() {} // in A
+}
+
+mixin M implements A {
+ m() {} // in M
+}
+''');
+ await prepareOverrides();
+ assertHasOverride('m() {} // in M');
+ assertNoSuperMember();
+ assertHasInterfaceMember('m() {} // in A');
+ }
+
Future<void> test_mixin_method_direct() async {
addTestFile('''
class A {
@@ -475,173 +973,19 @@
assertNoInterfaceMembers();
}
- Future<void> test_staticMembers() async {
- addTestFile('''
-class A {
- static int F = 0;
- static void M() {}
- static int get G => 0;
- static void set S(int v) {}
-}
-class B extends A {
- static int F = 0;
- static void M() {}
- static int get G => 0;
- static void set S(int v) {}
-}
-''');
- await prepareOverrides();
- expect(overridesList, isEmpty);
- }
-
- Future<void> test_super_fieldByField() async {
- addTestFile('''
-class A {
- int fff; // in A
-}
-class B extends A {
- int fff; // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('fff; // in B');
- assertHasSuperElement('fff; // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_fieldByGetter() async {
- addTestFile('''
-class A {
- int fff; // in A
-}
-class B extends A {
- get fff => 0; // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('fff => 0; // in B');
- assertHasSuperElement('fff; // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_fieldBySetter() async {
- addTestFile('''
-class A {
- int fff; // in A
-}
-class B extends A {
- set fff(x) {} // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('fff(x) {} // in B');
- assertHasSuperElement('fff; // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_getterByField() async {
- addTestFile('''
-class A {
- get fff => 0; // in A
- set fff(x) {} // in A
-}
-class B extends A {
- int fff; // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('fff; // in B');
- assertHasSuperElement('fff => 0; // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_getterByGetter() async {
- addTestFile('''
-class A {
- get fff => 0; // in A
-}
-class B extends A {
- get fff => 0; // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('fff => 0; // in B');
- assertHasSuperElement('fff => 0; // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_method_direct() async {
+ Future<void> test_mixin_superclassConstraint_method_direct() async {
addTestFile('''
class A {
m() {} // in A
}
-class B extends A {
- m() {} // in B
+
+mixin M on A {
+ m() {} // in M
}
''');
await prepareOverrides();
- assertHasOverride('m() {} // in B');
+ assertHasOverride('m() {} // in M');
assertHasSuperElement('m() {} // in A');
assertNoInterfaceMembers();
}
-
- Future<void> test_super_method_indirect() async {
- addTestFile('''
-class A {
- m() {} // in A
-}
-class B extends A {
-}
-class C extends B {
- m() {} // in C
-}
-''');
- await prepareOverrides();
- assertHasOverride('m() {} // in C');
- assertHasSuperElement('m() {} // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_method_privateByPrivate() async {
- addTestFile('''
-class A {
- _m() {} // in A
-}
-class B extends A {
- _m() {} // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('_m() {} // in B');
- assertHasSuperElement('_m() {} // in A');
- assertNoInterfaceMembers();
- }
-
- Future<void> test_super_method_superTypeCycle() async {
- addTestFile('''
-class A extends B {
- m() {} // in A
-}
-class B extends A {
- m() {} // in B
-}
-''');
- await prepareOverrides();
- // must finish
- }
-
- Future<void> test_super_setterBySetter() async {
- addTestFile('''
-class A {
- set fff(x) {} // in A
-}
-class B extends A {
- set fff(x) {} // in B
-}
-''');
- await prepareOverrides();
- assertHasOverride('fff(x) {} // in B');
- assertHasSuperElement('fff(x) {} // in A');
- assertNoInterfaceMembers();
- }
}
diff --git a/pkg/analysis_server/test/services/search/search_engine_test.dart b/pkg/analysis_server/test/services/search/search_engine_test.dart
index 30fa39f..0097edb 100644
--- a/pkg/analysis_server/test/services/search/search_engine_test.dart
+++ b/pkg/analysis_server/test/services/search/search_engine_test.dart
@@ -61,7 +61,7 @@
return SearchEngineImpl(allDrivers);
}
- Future<void> test_membersOfSubtypes_hasMembers() async {
+ Future<void> test_membersOfSubtypes_classByClass_hasMembers() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
void a() {}
@@ -91,6 +91,40 @@
expect(members, unorderedEquals(['a', 'b']));
}
+ Future<void> test_membersOfSubtypes_enum_implements_hasMembers() async {
+ await resolveTestCode('''
+class A {
+ void foo() {}
+}
+
+enum E implements A {
+ v;
+ void foo() {}
+}
+''');
+
+ var A = findElement.class_('A');
+ var members = await searchEngine.membersOfSubtypes(A);
+ expect(members, unorderedEquals(['foo']));
+ }
+
+ Future<void> test_membersOfSubtypes_enum_with_hasMembers() async {
+ await resolveTestCode('''
+mixin M {
+ void foo() {}
+}
+
+enum E with M {
+ v;
+ void foo() {}
+}
+''');
+
+ var M = findElement.mixin('M');
+ var members = await searchEngine.membersOfSubtypes(M);
+ expect(members, unorderedEquals(['foo']));
+ }
+
Future<void> test_membersOfSubtypes_noMembers() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_leading_underscore_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_leading_underscore_test.dart
new file mode 100644
index 0000000..adf0a5a
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_leading_underscore_test.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(RemoveLeadingUnderscoreBulkTest);
+ defineReflectiveTests(RemoveLeadingUnderscoreTest);
+ });
+}
+
+@reflectiveTest
+class RemoveLeadingUnderscoreBulkTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.no_leading_underscores_for_local_identifiers;
+
+ Future<void> test_singleFile() async {
+ await resolveTestCode('''
+main() {
+ int _foo = 42;
+ print(_foo);
+ [0, 1, 2].forEach((_bar) {
+ print(_bar);
+ });
+}
+''');
+ await assertHasFix('''
+main() {
+ int foo = 42;
+ print(foo);
+ [0, 1, 2].forEach((bar) {
+ print(bar);
+ });
+}
+''');
+ }
+}
+
+@reflectiveTest
+class RemoveLeadingUnderscoreTest extends FixProcessorLintTest {
+ @override
+ FixKind get kind => DartFixKind.REMOVE_LEADING_UNDERSCORE;
+
+ @override
+ String get lintCode => LintNames.no_leading_underscores_for_local_identifiers;
+
+ Future<void> test_localVariable() async {
+ await resolveTestCode('''
+void f() {
+ var _foo = 1;
+ print(_foo);
+}
+''');
+ await assertHasFix('''
+void f() {
+ var foo = 1;
+ print(foo);
+}
+''');
+ }
+
+ Future<void> test_parameter_closure() async {
+ await resolveTestCode('''
+void f() {
+ [0, 1, 2].forEach((_foo) {
+ print(_foo);
+ });
+}
+''');
+ await assertHasFix('''
+void f() {
+ [0, 1, 2].forEach((foo) {
+ print(foo);
+ });
+}
+''');
+ }
+
+ Future<void> test_parameter_function() async {
+ await resolveTestCode('''
+void f(int _foo) {
+ print(_foo);
+}
+''');
+ await assertHasFix('''
+void f(int foo) {
+ print(foo);
+}
+''');
+ }
+
+ Future<void> test_parameter_method() async {
+ await resolveTestCode('''
+class A {
+ void f(int _foo) {
+ print(_foo);
+ }
+}
+''');
+ await assertHasFix('''
+class A {
+ void f(int foo) {
+ print(foo);
+ }
+}
+''');
+ }
+
+ Future<void> test_parameter_optionalNamed() async {
+ await resolveTestCode('''
+void f({int? _foo}) {
+ print(_foo);
+}
+''');
+ await assertNoFix();
+ }
+
+ Future<void> test_parameter_optionalPositional() async {
+ await resolveTestCode('''
+void f([int? _foo]) {
+ print(_foo);
+}
+''');
+ await assertHasFix('''
+void f([int? foo]) {
+ print(foo);
+}
+''');
+ }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index c61f1a5..c1d46b5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -144,6 +144,7 @@
import 'remove_if_null_operator_test.dart' as remove_if_null_operator;
import 'remove_initializer_test.dart' as remove_initializer;
import 'remove_interpolation_braces_test.dart' as remove_interpolation_braces;
+import 'remove_leading_underscore_test.dart' as remove_leading_underscore;
import 'remove_method_declaration_test.dart' as remove_method_declaration;
import 'remove_name_from_combinator_test.dart' as remove_name_from_combinator;
import 'remove_non_null_assertion_test.dart' as remove_non_null_assertion_test;
@@ -346,6 +347,7 @@
remove_if_null_operator.main();
remove_initializer.main();
remove_interpolation_braces.main();
+ remove_leading_underscore.main();
remove_method_declaration.main();
remove_name_from_combinator.main();
remove_non_null_assertion_test.main();
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index a79fe72..84644e4 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -82,7 +82,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 208;
+ static const int DATA_VERSION = 209;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/index.dart b/pkg/analyzer/lib/src/dart/analysis/index.dart
index ba8cdd5..74c3887 100644
--- a/pkg/analyzer/lib/src/dart/analysis/index.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/index.dart
@@ -672,6 +672,20 @@
}
@override
+ void visitEnumDeclaration(EnumDeclaration node) {
+ _addSubtype(
+ node.name.name,
+ withClause: node.withClause,
+ implementsClause: node.implementsClause,
+ memberNodes: node.members,
+ );
+
+ var declaredElement = node.declaredElement!;
+ recordIsAncestorOf(declaredElement);
+ super.visitEnumDeclaration(node);
+ }
+
+ @override
void visitExportDirective(ExportDirective node) {
ExportElement? element = node.element;
recordUriReference(element?.exportedLibrary, node.uri);
diff --git a/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart b/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart
index 073d6f6..0cc56cb 100644
--- a/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart
@@ -41,6 +41,9 @@
_addSubtypedName(declaration.superclass);
_addSubtypedNames(declaration.withClause.mixinTypes);
_addSubtypedNames(declaration.implementsClause?.interfaces);
+ } else if (declaration is EnumDeclaration) {
+ _addSubtypedNames(declaration.withClause?.mixinTypes);
+ _addSubtypedNames(declaration.implementsClause?.interfaces);
} else if (declaration is MixinDeclaration) {
_addSubtypedNames(declaration.onClause?.superclassConstraints);
_addSubtypedNames(declaration.implementsClause?.interfaces);
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index 53650c1..3cc2617 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -473,7 +473,7 @@
);
}
- test_getFilesSubtypingName() {
+ test_getFilesSubtypingName_class() {
String a = convertPath('/a.dart');
String b = convertPath('/b.dart');
@@ -510,6 +510,75 @@
);
}
+ test_getFilesSubtypingName_enum_implements() {
+ String a = convertPath('/a.dart');
+ String b = convertPath('/b.dart');
+
+ newFile(a, content: r'''
+class A {}
+enum E1 implements A {
+ v
+}
+''');
+ newFile(b, content: r'''
+class A {}
+enum E2 implements A {
+ v
+}
+''');
+
+ FileState aFile = fileSystemState.getFileForPath(a);
+ FileState bFile = fileSystemState.getFileForPath(b);
+
+ expect(
+ fileSystemState.getFilesSubtypingName('A'),
+ unorderedEquals([aFile, bFile]),
+ );
+
+ // Change b.dart so that it does not subtype A.
+ newFile(b, content: r'''
+class C {}
+enum E2 implements C {
+ v
+}
+''');
+ bFile.refresh();
+ expect(
+ fileSystemState.getFilesSubtypingName('A'),
+ unorderedEquals([aFile]),
+ );
+ expect(
+ fileSystemState.getFilesSubtypingName('C'),
+ unorderedEquals([bFile]),
+ );
+ }
+
+ test_getFilesSubtypingName_enum_with() {
+ String a = convertPath('/a.dart');
+ String b = convertPath('/b.dart');
+
+ newFile(a, content: r'''
+mixin M {}
+enum E1 with M {
+ v
+}
+''');
+ newFile(b, content: r'''
+mixin M {}
+enum E2 with M {
+ v
+}
+''');
+
+ FileState aFile = fileSystemState.getFileForPath(a);
+ FileState bFile = fileSystemState.getFileForPath(b);
+
+ expect(
+ fileSystemState.getFilesSubtypingName('M'),
+ unorderedEquals([aFile, bFile]),
+ );
+ }
+
test_hasUri() {
Uri uri = Uri.parse('package:aaa/foo.dart');
String templatePath = convertPath('/aaa/lib/foo.dart');
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
index 568de01..4c31935 100644
--- a/pkg/analyzer/test/src/dart/analysis/index_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -1747,6 +1747,36 @@
expect(index.subtypes, isEmpty);
}
+ test_subtypes_enum_implements() async {
+ String libP = 'package:test/test.dart;package:test/test.dart';
+ await _indexTestUnit('''
+class A {}
+
+enum E implements A {
+ v;
+ void foo() {}
+}
+''');
+
+ expect(index.subtypes, hasLength(1));
+ _assertSubtype(0, '$libP;A', 'E', ['foo']);
+ }
+
+ test_subtypes_enum_with() async {
+ String libP = 'package:test/test.dart;package:test/test.dart';
+ await _indexTestUnit('''
+mixin M {}
+
+enum E with M {
+ v;
+ void foo() {}
+}
+''');
+
+ expect(index.subtypes, hasLength(1));
+ _assertSubtype(0, '$libP;M', 'E', ['foo']);
+ }
+
test_subtypes_mixinDeclaration() async {
String libP = 'package:test/lib.dart;package:test/lib.dart';
newFile('$testPackageLibPath/lib.dart', content: '''
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index 3410fd2..6c5e71d 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -2287,7 +2287,7 @@
await _verifyReferences(element, expected);
}
- test_subtypes() async {
+ test_subtypes_class() async {
await resolveTestCode('''
class A {}
@@ -2342,7 +2342,7 @@
}
}
- test_subtypes_discover() async {
+ test_subtypes_class_discover() async {
var aaaPackageRootPath = '$packagesRootPath/aaa';
var bbbPackageRootPath = '$packagesRootPath/bbb';
@@ -2412,7 +2412,7 @@
expect(b.members, ['method1']);
}
- test_subTypes_discover() async {
+ test_subTypes_class_discover() async {
var aaaPackageRootPath = '$packagesRootPath/aaa';
var bbbPackageRootPath = '$packagesRootPath/bbb';
var cccPackageRootPath = '$packagesRootPath/ccc';
@@ -2453,7 +2453,7 @@
assertHasResult(cccFilePath, 'C', not: true);
}
- test_subtypes_files() async {
+ test_subtypes_class_files() async {
String pathB = convertPath('$testPackageLibPath/b.dart');
String pathC = convertPath('$testPackageLibPath/c.dart');
newFile(pathB, content: r'''
@@ -2482,6 +2482,59 @@
expect(c.id, endsWith('c.dart;C'));
}
+ test_subtypes_class_partWithoutLibrary() async {
+ await resolveTestCode('''
+part of lib;
+
+class A {}
+class B extends A {}
+''');
+ var a = findElement.class_('A');
+
+ List<SubtypeResult> subtypes =
+ await driver.search.subtypes(SearchedFiles(), type: a);
+ expect(subtypes, hasLength(1));
+
+ SubtypeResult b = subtypes.singleWhere((r) => r.name == 'B');
+ expect(b.libraryUri, testUriStr);
+ expect(b.id, '$testUriStr;$testUriStr;B');
+ }
+
+ test_subtypes_enum() async {
+ await resolveTestCode('''
+class A {}
+
+enum E1 implements A {
+ v;
+ void methodE1() {}
+}
+
+enum E2 with A {
+ v;
+ void methodE2() {}
+}
+
+class B {}
+''');
+
+ var subtypes = await driver.search.subtypes(
+ SearchedFiles(),
+ type: findElement.class_('A'),
+ );
+ expect(subtypes, hasLength(2));
+
+ var resultE1 = subtypes.singleWhere((r) => r.name == 'E1');
+ var resultE2 = subtypes.singleWhere((r) => r.name == 'E2');
+
+ expect(resultE1.libraryUri, testUriStr);
+ expect(resultE1.id, '$testUriStr;$testUriStr;E1');
+ expect(resultE1.members, ['methodE1']);
+
+ expect(resultE2.libraryUri, testUriStr);
+ expect(resultE2.id, '$testUriStr;$testUriStr;E2');
+ expect(resultE2.members, ['methodE2']);
+ }
+
test_subtypes_mixin_superclassConstraints() async {
await resolveTestCode('''
class A {
@@ -2521,24 +2574,6 @@
}
}
- test_subtypes_partWithoutLibrary() async {
- await resolveTestCode('''
-part of lib;
-
-class A {}
-class B extends A {}
-''');
- var a = findElement.class_('A');
-
- List<SubtypeResult> subtypes =
- await driver.search.subtypes(SearchedFiles(), type: a);
- expect(subtypes, hasLength(1));
-
- SubtypeResult b = subtypes.singleWhere((r) => r.name == 'B');
- expect(b.libraryUri, testUriStr);
- expect(b.id, '$testUriStr;$testUriStr;B');
- }
-
test_topLevelElements() async {
await resolveTestCode('''
class A {} // A
diff --git a/pkg/dart2wasm/bin/run_wasm.js b/pkg/dart2wasm/bin/run_wasm.js
index 08a6d92..7e962a0 100644
--- a/pkg/dart2wasm/bin/run_wasm.js
+++ b/pkg/dart2wasm/bin/run_wasm.js
@@ -46,6 +46,19 @@
setTimeout(function() {
inst.exports.$call0(closure);
}, milliseconds);
+ },
+ getCurrentStackTrace: function() {
+ // [Error] should be supported in most browsers.
+ // A possible future optimization we could do is to just save the
+ // `Error` object here, and stringify the stack trace when it is
+ // actually used.
+ let stackString = new Error().stack.toString();
+
+ // We remove the last three lines of the stack trace to prevent including
+ // `Error`, `getCurrentStackTrace`, and `StackTrace.current` in the
+ // stack trace.
+ let userStackString = stackString.split('\n').slice(3).join('\n');
+ return stringToDartString(userStackString);
}
};
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index d4ef423..5ae0b2a 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -49,6 +49,7 @@
w.Local? preciseThisLocal;
final Map<TypeParameter, w.Local> typeLocals = {};
final List<Statement> finalizers = [];
+ final List<w.Label> tryLabels = [];
final Map<LabeledStatement, w.Label> labels = {};
final Map<SwitchCase, w.Label> switchLabels = {};
@@ -537,8 +538,86 @@
@override
void visitTryCatch(TryCatch node) {
- // TODO(joshualitt): Include catches
+ // It is not valid dart to have a try without a catch.
+ assert(node.catches.isNotEmpty);
+
+ // We lower a [TryCatch] to a wasm try block. If there are any [Catch]
+ // nodes, we generate a single wasm catch instruction, and dispatch at
+ // runtime based on the type of the caught exception.
+ w.Label try_ = b.try_();
node.body.accept(this);
+ b.br(try_);
+
+ // Insert a catch instruction which will catch any thrown Dart
+ // exceptions.
+ // Note: We must wait to add the try block to the [tryLabels] stack until
+ // after we have visited the body of the try. This is to handle the case of
+ // a rethrow nested within a try nested within a catch, that is we need the
+ // rethrow to target the last try block with a catch.
+ tryLabels.add(try_);
+ b.catch_(translator.exceptionTag);
+
+ // Stash the original exception in a local so we can push it back onto the
+ // stack after each type test. Also, store the stack trace in a local.
+ w.RefType stackTrace = translator.stackTraceInfo.nonNullableType;
+ w.Local thrownStackTrace = addLocal(stackTrace);
+ b.local_set(thrownStackTrace);
+
+ w.RefType exception = translator.topInfo.nonNullableType;
+ w.Local thrownException = addLocal(exception);
+ b.local_set(thrownException);
+
+ // For each catch node:
+ // 1) Create a block for the catch.
+ // 2) Push the caught exception onto the stack.
+ // 3) Add a type test based on the guard of the catch.
+ // 4) If the test fails, we jump to the next catch. Otherwise, we
+ // execute the body of the catch.
+ for (Catch catch_ in node.catches) {
+ w.Label catchBlock = b.block();
+ DartType guard = catch_.guard;
+
+ // Only emit the type test if the guard is not [Object].
+ if (guard != translator.coreTypes.objectNonNullableRawType) {
+ b.local_get(thrownException);
+ emitTypeTest(
+ guard, translator.coreTypes.objectNonNullableRawType, node);
+ b.i32_eqz();
+ b.br_if(catchBlock);
+ }
+
+ // If there is an exception declaration, create a local corresponding to
+ // the catch's exception [VariableDeclaration] node.
+ VariableDeclaration? exceptionDeclaration = catch_.exception;
+ if (exceptionDeclaration != null) {
+ w.Local guardedException = addLocal(exception);
+ locals[exceptionDeclaration] = guardedException;
+ b.local_get(thrownException);
+ b.local_set(guardedException);
+ }
+
+ // If there is a stack trace declaration, then create a local
+ // corresponding to the catch's stack trace [VariableDeclaration] node.
+ VariableDeclaration? stackTraceDeclaration = catch_.stackTrace;
+ if (stackTraceDeclaration != null) {
+ w.Local guardedStackTrace = addLocal(stackTrace);
+ locals[stackTraceDeclaration] = guardedStackTrace;
+ b.local_get(thrownStackTrace);
+ b.local_set(guardedStackTrace);
+ }
+ catch_.body.accept(this);
+
+ // Jump out of the try entirely if we enter any catch block.
+ b.br(try_);
+ b.end(); // end catchBlock.
+ }
+
+ // We insert a rethrow just before the end of the try block to handle the
+ // case where none of the catch blocks catch the type of the thrown
+ // exception.
+ b.rethrow_(try_);
+ tryLabels.removeLast();
+ b.end(); // end try_.
}
@override
@@ -1639,13 +1718,18 @@
@override
w.ValueType visitThrow(Throw node, w.ValueType expectedType) {
- wrap(node.expression, translator.topInfo.nullableType);
- // TODO(joshualitt): Throw exception
- b.comment(node.toStringInternal());
- b.drop();
- b.block(const [], [if (expectedType != voidMarker) expectedType]);
- b.unreachable();
- b.end();
+ wrap(node.expression, translator.topInfo.nonNullableType);
+ _call(translator.stackTraceCurrent.reference);
+
+ // At this point, we have the exception and the current stack trace on the
+ // stack, so just throw them using the exception tag.
+ b.throw_(translator.exceptionTag);
+ return expectedType;
+ }
+
+ @override
+ w.ValueType visitRethrow(Rethrow node, w.ValueType expectedType) {
+ b.rethrow_(tryLabels.last);
return expectedType;
}
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 7a33ce4..36a4b0d 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -85,6 +85,8 @@
late final Class typedListClass;
late final Class typedListViewClass;
late final Class byteDataViewClass;
+ late final Class stackTraceClass;
+ late final Procedure stackTraceCurrent;
late final Procedure stringEquals;
late final Procedure stringInterpolate;
late final Procedure mapFactory;
@@ -110,6 +112,7 @@
late final w.Module m;
late final w.DefinedFunction initFunction;
late final w.ValueType voidMarker;
+ late final w.Tag exceptionTag = createExceptionTag();
// Caches for when identical source constructs need a common representation.
final Map<w.StorageType, w.ArrayType> arrayTypeCache = {};
@@ -121,6 +124,7 @@
ClassInfo get topInfo => classes[0];
ClassInfo get objectInfo => classInfo[coreTypes.objectClass]!;
+ ClassInfo get stackTraceInfo => classInfo[stackTraceClass]!;
Translator(this.component, this.coreTypes, this.typeEnvironment, this.options)
: libraries = component.libraries,
@@ -172,10 +176,13 @@
oneByteStringClass = lookupCore("_OneByteString");
twoByteStringClass = lookupCore("_TwoByteString");
typeClass = lookupCore("_Type");
+ stackTraceClass = lookupCore("StackTrace");
typedListBaseClass = lookupTypedData("_TypedListBase");
typedListClass = lookupTypedData("_TypedList");
typedListViewClass = lookupTypedData("_TypedListView");
byteDataViewClass = lookupTypedData("_ByteDataView");
+ stackTraceCurrent =
+ stackTraceClass.procedures.firstWhere((p) => p.name.text == "current");
stringEquals =
stringBaseClass.procedures.firstWhere((p) => p.name.text == "==");
stringInterpolate = stringBaseClass.procedures
@@ -340,6 +347,17 @@
: coreTypes.objectClass;
}
+ /// Creates a [Tag] for a void [FunctionType] with two parameters,
+ /// a [topInfo.nonNullableType] parameter to hold an exception, and a
+ /// [stackTraceInfo.nonNullableType] to hold a stack trace. This single
+ /// exception tag is used to throw and catch all Dart exceptions.
+ w.Tag createExceptionTag() {
+ w.FunctionType functionType = m.addFunctionType(
+ [topInfo.nonNullableType, stackTraceInfo.nonNullableType], const []);
+ w.Tag tag = m.addTag(functionType);
+ return tag;
+ }
+
w.ValueType translateType(DartType type) {
w.StorageType wasmType = translateStorageType(type);
if (wasmType is w.ValueType) return wasmType;
diff --git a/pkg/dartdev/lib/src/commands/doc.dart b/pkg/dartdev/lib/src/commands/doc.dart
index 9a9c4a2..fb9d38a 100644
--- a/pkg/dartdev/lib/src/commands/doc.dart
+++ b/pkg/dartdev/lib/src/commands/doc.dart
@@ -90,7 +90,7 @@
if (verbose) ...['--verbose-warnings', '--show-stats'],
]);
- final config = await parseOptions(pubPackageMetaProvider, options);
+ final config = parseOptions(pubPackageMetaProvider, options);
if (config == null) {
// There was an error while parsing options.
return 2;
@@ -105,7 +105,7 @@
config, pubPackageMetaProvider, packageConfigProvider);
final dartdoc = config.generateDocs
? await Dartdoc.fromContext(config, packageBuilder)
- : await Dartdoc.withEmptyGenerator(config, packageBuilder);
+ : Dartdoc.withEmptyGenerator(config, packageBuilder);
dartdoc.executeGuarded();
return 0;
}
diff --git a/pkg/dartdev/lib/src/templates/server_shelf.dart b/pkg/dartdev/lib/src/templates/server_shelf.dart
index 2292d39..023ffea 100644
--- a/pkg/dartdev/lib/src/templates/server_shelf.dart
+++ b/pkg/dartdev/lib/src/templates/server_shelf.dart
@@ -11,7 +11,7 @@
: super(
'server-shelf',
'Server app',
- 'A server app using `package:shelf`',
+ 'A server app using package:shelf.',
categories: const ['dart', 'server'],
) {
addFile('.gitignore', common.gitignore);
@@ -127,7 +127,7 @@
}
Response _echoHandler(Request request) {
- final message = params(request, 'message');
+ final message = request.params['message'];
return Response.ok('$message\n');
}
diff --git a/pkg/dartdev/test/commands/create_integration_test.dart b/pkg/dartdev/test/commands/create_integration_test.dart
index 92e981a..19f93d8 100644
--- a/pkg/dartdev/test/commands/create_integration_test.dart
+++ b/pkg/dartdev/test/commands/create_integration_test.dart
@@ -2,9 +2,13 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'dart:async';
+import 'dart:convert';
import 'dart:io';
import 'package:dartdev/src/commands/create.dart';
+import 'package:dartdev/src/templates.dart';
+import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import '../utils.dart';
@@ -14,42 +18,134 @@
}
void defineCreateTests() {
- TestProject? p;
+ TestProject? proj;
- setUp(() => p = null);
+ setUp(() => proj = null);
- tearDown(() async => await p?.dispose());
+ tearDown(() async => await proj?.dispose());
// Create tests for each template.
for (String templateId
in CreateCommand.legalTemplateIds(includeDeprecated: true)) {
test(templateId, () async {
- p = project();
+ const projectName = 'template_project';
+ proj = project();
+ final p = proj!;
+ final templateGenerator = getGenerator(templateId)!;
- ProcessResult createResult = await p!.run([
+ ProcessResult createResult = await p.run([
'create',
'--force',
'--template',
templateId,
- 'template_project',
+ projectName,
]);
expect(createResult.exitCode, 0, reason: createResult.stderr);
// Validate that the project analyzes cleanly.
- // TODO: Should we use --fatal-infos here?
- ProcessResult analyzeResult =
- await p!.run(['analyze'], workingDir: p!.dir.path);
+ ProcessResult analyzeResult = await p.run(
+ ['analyze', '--fatal-infos', projectName],
+ workingDir: p.dir.path,
+ );
expect(analyzeResult.exitCode, 0, reason: analyzeResult.stdout);
// Validate that the code is well formatted.
- ProcessResult formatResult = await p!.run([
+ ProcessResult formatResult = await p.run([
'format',
'--output',
'none',
'--set-exit-if-changed',
- 'template_project',
+ projectName,
]);
expect(formatResult.exitCode, 0, reason: formatResult.stdout);
+
+ // Process the execution instructions provided by the template.
+ final runCommands = templateGenerator
+ .getInstallInstructions(
+ projectName,
+ scriptPath: projectName,
+ )
+ .split('\n')
+ // Remove directory change instructions.
+ .sublist(1)
+ .map((command) => command.trim())
+ .map((command) {
+ final commandParts = command.split(' ');
+ if (command.startsWith('dart ')) {
+ return commandParts.sublist(1);
+ }
+ return commandParts;
+ }).toList();
+
+ final isServerTemplate = templateGenerator.categories.contains('server');
+ final isWebTemplate = templateGenerator.categories.contains('web');
+ final workingDir = path.join(p.dirPath, projectName);
+
+ // Execute the templates run instructions.
+ for (int i = 0; i < runCommands.length; ++i) {
+ // The last command is always the command to execute the code generated
+ // by the template.
+ final isLastCommand = i == runCommands.length - 1;
+ final command = runCommands[i];
+ Process process;
+
+ if (isLastCommand && isWebTemplate) {
+ // The web template uses `webdev` to execute, not `dart`, so don't
+ // run the test through the project utility method.
+ process = await Process.start(
+ path.join(
+ p.pubCacheBinPath,
+ Platform.isWindows ? '${command.first}.bat' : command.first,
+ ),
+ command.sublist(1),
+ workingDirectory: workingDir,
+ environment: {
+ 'PUB_CACHE': p.pubCachePath,
+ 'PATH': path.dirname(Platform.resolvedExecutable) +
+ (Platform.isWindows ? ';' : ':') +
+ Platform.environment['PATH']!,
+ });
+ } else {
+ process = await p.start(
+ command,
+ workingDir: workingDir,
+ );
+ }
+
+ if (isLastCommand && (isServerTemplate || isWebTemplate)) {
+ final completer = Completer<void>();
+ late StreamSubscription stdoutSub;
+ late StreamSubscription stderrSub;
+ // Listen for well-known output from specific templates to determine
+ // if they've executed correctly. These templates won't exit on their
+ // own, so we'll need to terminate the process once we've verified it
+ // runs correctly.
+ stdoutSub = process.stdout.transform(utf8.decoder).listen((e) {
+ print('stdout: $e');
+ if ((isServerTemplate && e.contains('Server listening on port')) ||
+ (isWebTemplate && e.contains('Succeeded after'))) {
+ stderrSub.cancel();
+ stdoutSub.cancel();
+ process.kill();
+ completer.complete();
+ }
+ });
+ stderrSub = process.stderr
+ .transform(utf8.decoder)
+ .listen((e) => print('stderr: $e'));
+ await completer.future;
+
+ // Since we had to terminate the process manually, we aren't certain
+ // as to what the exit code will be on all platforms (should be -15
+ // for POSIX systems), so we'll just wait for the process to exit
+ // here.
+ await process.exitCode;
+ } else {
+ // If the sample should exit on its own, it should always result in
+ // an exit code of 0.
+ expect(await process.exitCode, 0);
+ }
+ }
});
}
}
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index 145d519..996b98b 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -45,6 +45,10 @@
String get dirPath => dir.path;
+ String get pubCachePath => path.join(dirPath, 'pub_cache');
+
+ String get pubCacheBinPath => path.join(pubCachePath, 'bin');
+
String get mainPath => path.join(dirPath, relativeFilePath);
final String name;
@@ -129,7 +133,10 @@
...arguments,
],
workingDirectory: workingDir ?? dir.path,
- environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
+ environment: {
+ if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true',
+ 'PUB_CACHE': pubCachePath,
+ });
final proc = _process!;
final stdoutContents = proc.stdout.transform(utf8.decoder).join();
final stderrContents = proc.stderr.transform(utf8.decoder).join();
@@ -153,7 +160,10 @@
...arguments,
],
workingDirectory: workingDir ?? dir.path,
- environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'})
+ environment: {
+ if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true',
+ 'PUB_CACHE': pubCachePath,
+ })
..then((p) => _process = p);
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart
index 0e9f780..7db89ce 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart
@@ -315,6 +315,7 @@
'Symbol(C.field): 42, Symbol(_field): 0}');
});
});
+
group('Named arguments anywhere', () {
var source = r'''
String topLevelMethod(int param1, String param2,
@@ -390,6 +391,155 @@
expectedResult: '1, two, 3, four');
});
});
+
+ group('Enums', () {
+ var source = r'''
+ enum E {id1, id2, id3}
+
+ enum E2 {id1, id2, id3}
+
+ main() {
+ var e = E.id2;
+ // Breakpoint: bp
+ print('hello world');
+ }
+ ''';
+
+ setUpAll(() async {
+ await driver.initSource(setup, source);
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('evaluate to the correct string', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E.id2.toString()',
+ expectedResult: 'E.id2');
+ });
+ test('evaluate to the correct index', () async {
+ await driver.check(
+ breakpointId: 'bp', expression: 'E.id3.index', expectedResult: '2');
+ });
+ test('compare properly against themselves', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'e == E.id2 && E.id2 == E.id2',
+ expectedResult: 'true');
+ });
+ test('compare properly against other enums', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'e != E2.id2 && E.id2 != E2.id2',
+ expectedResult: 'true');
+ });
+ });
+
+ group('Enhanced enums', () {
+ var source = r'''
+ enum E<T> with M {
+ id_int<int>(0),
+ id_bool<bool>(true),
+ id_string<String>('hello world', n: 13);
+
+ final T field;
+ final num n;
+ static const constStaticField = id_string;
+
+ const E(T arg0, {num? n}) : this.field = arg0, this.n = n ?? 42;
+
+ T get fieldGetter => field;
+ num instanceMethod() => n;
+ }
+
+ enum E2 with M {
+ v1, v2, id_string;
+ int get index => 10;
+ }
+
+ mixin M on Enum {
+ int mixinMethod() => index * 100;
+ }
+
+ main() {
+ var e = E.id_string;
+ // Breakpoint: bp
+ print('hello world');
+ }
+ ''';
+
+ setUpAll(() async {
+ await driver
+ .initSource(setup, source, experiments: {'enhanced-enums': true});
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('evaluate to the correct string', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E.id_string.toString()',
+ expectedResult: 'E.id_string');
+ });
+ test('evaluate to the correct index', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E.id_string.index',
+ expectedResult: '2');
+ });
+ test('compare properly against themselves', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'e == E.id_string && E.id_string == E.id_string',
+ expectedResult: 'true');
+ });
+ test('compare properly against other enums', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'e != E2.id_string && E.id_string != E2.id_string',
+ expectedResult: 'true');
+ });
+ test('with instance methods', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E.id_bool.instanceMethod()',
+ expectedResult: '42');
+ });
+ test('with instance methods from local instance', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'e.instanceMethod()',
+ expectedResult: '13');
+ });
+ test('with getters', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E.id_int.fieldGetter',
+ expectedResult: '0');
+ });
+ test('with getters from local instance', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'e.fieldGetter',
+ expectedResult: 'hello world');
+ });
+ test('with mixin calls', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E.id_string.mixinMethod()',
+ expectedResult: '200');
+ });
+ test('with mixin calls through overridden indices', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'E2.v2.mixinMethod()',
+ expectedResult: '1000');
+ });
+ });
}
/// Shared tests that are valid in legacy (before 2.12) and are agnostic to
diff --git a/pkg/front_end/lib/src/api_prototype/compiler_options.dart b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
index 5a6fc48..3bd1f30 100644
--- a/pkg/front_end/lib/src/api_prototype/compiler_options.dart
+++ b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
@@ -19,6 +19,7 @@
import '../base/nnbd_mode.dart';
import '../fasta/kernel/macro.dart';
+import '../macro_serializer.dart';
import 'experimental_flags.dart'
show
AllowedExperimentalFlags,
@@ -137,7 +138,7 @@
/// by the macro executor provided by [macroExecutorProvider].
///
/// This is part of the experimental macro feature.
- Future<Uri> Function(Component)? macroSerializer;
+ MacroSerializer? macroSerializer;
/// Whether to generate code for the SDK.
///
diff --git a/pkg/front_end/lib/src/isolate_macro_serializer.dart b/pkg/front_end/lib/src/isolate_macro_serializer.dart
new file mode 100644
index 0000000..ef98905
--- /dev/null
+++ b/pkg/front_end/lib/src/isolate_macro_serializer.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:isolate';
+
+import 'package:kernel/kernel.dart';
+import 'macro_serializer.dart';
+
+/// [MacroSerializer] that uses blobs registered with the current [Isolate] to
+/// give access to precompiled macro [Component]s.
+///
+/// This can only be used with the [Isolate]-based macro executor.
+class IsolateMacroSerializer implements MacroSerializer {
+ final List<Uri> _createdUris = [];
+
+ @override
+ Future<void> close() {
+ for (Uri uri in _createdUris) {
+ (Isolate.current as dynamic).unregisterKernelBlobUri(uri);
+ }
+ _createdUris.clear();
+ return new Future.value();
+ }
+
+ @override
+ Future<Uri> createUriForComponent(Component component) {
+ Uri uri = (Isolate.current as dynamic)
+ .createUriForKernelBlob(writeComponentToBytes(component));
+ _createdUris.add(uri);
+ return new Future.value(uri);
+ }
+}
diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart
index 8801b72..dc552e1 100644
--- a/pkg/front_end/lib/src/kernel_generator_impl.dart
+++ b/pkg/front_end/lib/src/kernel_generator_impl.dart
@@ -303,8 +303,8 @@
precompilationOptions..fileSystem = new HybridFileSystem(fs);
CompilerResult? compilerResult =
await kernelForProgramInternal(uri, precompilationOptions);
- Uri precompiledUri =
- await options.macroSerializer!(compilerResult!.component!);
+ Uri precompiledUri = await options.macroSerializer!
+ .createUriForComponent(compilerResult!.component!);
Map<MacroClass, Uri> precompiledMacroUris =
options.precompiledMacroUris ??= {};
neededPrecompilations.macroDeclarations
diff --git a/pkg/front_end/lib/src/macro_serializer.dart b/pkg/front_end/lib/src/macro_serializer.dart
new file mode 100644
index 0000000..a8e7ba7
--- /dev/null
+++ b/pkg/front_end/lib/src/macro_serializer.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart';
+
+/// Interface for supporting serialization of [Component]s for macro
+/// precompilation.
+abstract class MacroSerializer {
+ /// Returns a [Uri] that can be accessed by the macro executor.
+ Future<Uri> createUriForComponent(Component component);
+
+ /// Releases all resources of this serializer.
+ ///
+ /// This must be called when the [Uri]s created by [createUriForComponent]
+ /// are no longer needed.
+ Future<void> close();
+}
diff --git a/pkg/front_end/lib/src/temp_dir_macro_serializer.dart b/pkg/front_end/lib/src/temp_dir_macro_serializer.dart
new file mode 100644
index 0000000..c1630c8
--- /dev/null
+++ b/pkg/front_end/lib/src/temp_dir_macro_serializer.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/kernel.dart';
+
+import 'fasta/kernel/utils.dart';
+import 'macro_serializer.dart';
+
+/// [MacroSerializer] that uses .dill files stored in a temporary directory to
+/// provided [Uri]s for precompiled macro [Component]s.
+///
+/// This can be used other with the isolate and process based macro executors.
+class TempDirMacroSerializer implements MacroSerializer {
+ final String? name;
+ Directory? tempDirectory;
+ int precompiledCount = 0;
+
+ TempDirMacroSerializer([this.name]);
+
+ Future<Directory> _ensureDirectory() async {
+ return tempDirectory ??= await Directory.systemTemp.createTemp(name);
+ }
+
+ @override
+ Future<Uri> createUriForComponent(Component component) async {
+ Directory directory = await _ensureDirectory();
+ Uri uri =
+ directory.absolute.uri.resolve('macros${precompiledCount++}.dill');
+ await writeComponentToFile(component, uri);
+ return uri;
+ }
+
+ @override
+ Future<void> close() async {
+ try {
+ await tempDirectory?.delete(recursive: true);
+ } catch (_) {}
+ }
+}
diff --git a/pkg/front_end/test/macro_api_test.dart b/pkg/front_end/test/macro_api_test.dart
index c096d36..70c3503 100644
--- a/pkg/front_end/test/macro_api_test.dart
+++ b/pkg/front_end/test/macro_api_test.dart
@@ -2,29 +2,26 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:io' show Directory, Platform;
+import 'dart:io' show Platform;
-import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
as isolatedExecutor;
+import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
import 'package:expect/expect.dart';
import 'package:front_end/src/api_prototype/experimental_flags.dart';
import 'package:front_end/src/api_prototype/front_end.dart';
import 'package:front_end/src/compute_platform_binaries_location.dart';
import 'package:front_end/src/fasta/kernel/macro.dart';
-import 'package:front_end/src/fasta/kernel/utils.dart';
+import 'package:front_end/src/isolate_macro_serializer.dart';
+import 'package:front_end/src/macro_serializer.dart';
import 'package:front_end/src/testing/id_testing_helper.dart';
-import 'package:kernel/ast.dart' hide Arguments;
-import 'package:kernel/kernel.dart';
import 'package:kernel/target/targets.dart';
import 'package:vm/target/vm.dart';
Future<void> main(List<String> args) async {
enableMacros = true;
- Directory tempDirectory =
- await Directory.systemTemp.createTemp('macro_api_test');
- int precompiledCount = 0;
+ MacroSerializer macroSerializer = new IsolateMacroSerializer();
try {
CompilerOptions options = new CompilerOptions();
options.sdkRoot = computePlatformBinariesLocation();
@@ -37,12 +34,7 @@
};
options.precompiledMacroUris = {};
options.target = options.macroTarget = new VmTarget(new TargetFlags());
- options.macroSerializer = (Component component) async {
- Uri uri = tempDirectory.absolute.uri
- .resolve('macros${precompiledCount++}.dill');
- await writeComponentToFile(component, uri);
- return uri;
- };
+ options.macroSerializer = macroSerializer;
InternalCompilerResult result = await kernelForProgramInternal(
Platform.script.resolve(
@@ -51,6 +43,6 @@
retainDataForTesting: true) as InternalCompilerResult;
Expect.isFalse(result.kernelTargetForTesting!.loader.hasSeenError);
} finally {
- await tempDirectory.delete(recursive: true);
+ await macroSerializer.close();
}
}
diff --git a/pkg/front_end/test/macro_application/macro_application_test.dart b/pkg/front_end/test/macro_application/macro_application_test.dart
index 4974822..3b988bb 100644
--- a/pkg/front_end/test/macro_application/macro_application_test.dart
+++ b/pkg/front_end/test/macro_application/macro_application_test.dart
@@ -17,9 +17,10 @@
import 'package:front_end/src/fasta/builder/field_builder.dart';
import 'package:front_end/src/fasta/builder/member_builder.dart';
import 'package:front_end/src/fasta/kernel/macro.dart';
-import 'package:front_end/src/fasta/kernel/utils.dart';
import 'package:front_end/src/fasta/source/source_class_builder.dart';
import 'package:front_end/src/fasta/source/source_library_builder.dart';
+import 'package:front_end/src/macro_serializer.dart';
+import 'package:front_end/src/temp_dir_macro_serializer.dart';
import 'package:front_end/src/testing/compiler_common.dart';
import 'package:front_end/src/testing/id_extractor.dart';
import 'package:front_end/src/testing/id_testing_helper.dart';
@@ -36,8 +37,8 @@
bool generateExpectations = args.contains('-g');
enableMacros = true;
- Directory tempDirectory =
- await Directory.systemTemp.createTemp('macro_application');
+ MacroSerializer macroSerializer =
+ new TempDirMacroSerializer('macro_application');
try {
Directory dataDir =
new Directory.fromUri(Platform.script.resolve('data/tests'));
@@ -46,23 +47,22 @@
createUriForFileName: createUriForFileName,
onFailure: onFailure,
runTest: runTestFor(const MacroDataComputer(), [
- new MacroTestConfig(dataDir, tempDirectory,
+ new MacroTestConfig(dataDir, macroSerializer,
generateExpectations: generateExpectations)
]),
preserveWhitespaceInAnnotations: true);
} finally {
- await tempDirectory.delete(recursive: true);
+ await macroSerializer.close();
}
}
class MacroTestConfig extends TestConfig {
final Directory dataDir;
- final Directory tempDirectory;
+ final MacroSerializer macroSerializer;
final bool generateExpectations;
- int precompiledCount = 0;
final Map<MacroClass, Uri> precompiledMacroUris = {};
- MacroTestConfig(this.dataDir, this.tempDirectory,
+ MacroTestConfig(this.dataDir, this.macroSerializer,
{required this.generateExpectations})
: super(cfeMarker, 'cfe',
explicitExperimentalFlags: {ExperimentalFlag.macros: true},
@@ -76,12 +76,7 @@
};
options.precompiledMacroUris = precompiledMacroUris;
options.macroTarget = new VmTarget(new TargetFlags());
- options.macroSerializer = (Component component) async {
- Uri uri = tempDirectory.absolute.uri
- .resolve('macros${precompiledCount++}.dill');
- await writeComponentToFile(component, uri);
- return uri;
- };
+ options.macroSerializer = macroSerializer;
}
@override
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index e4445e6..cc0957e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -123,6 +123,7 @@
bk
blindly
blob
+blobs
blocking
bmp
bn
@@ -1393,6 +1394,7 @@
unpleasant
unqualified
unreachable
+unregister
unseen
unset
unshadowed
diff --git a/pkg/kernel/lib/kernel.dart b/pkg/kernel/lib/kernel.dart
index 9433890..e5787d9 100644
--- a/pkg/kernel/lib/kernel.dart
+++ b/pkg/kernel/lib/kernel.dart
@@ -13,6 +13,8 @@
///
library kernel;
+import 'dart:typed_data';
+
import 'ast.dart';
import 'binary/ast_to_binary.dart';
import 'binary/ast_from_binary.dart';
@@ -61,7 +63,7 @@
return future;
}
-List<int> writeComponentToBytes(Component component) {
+Uint8List writeComponentToBytes(Component component) {
BytesSink sink = new BytesSink();
new BinaryPrinter(sink).writeComponentFile(component);
return sink.builder.toBytes();
diff --git a/pkg/wasm_builder/lib/src/instructions.dart b/pkg/wasm_builder/lib/src/instructions.dart
index 24a9556..10df25a 100644
--- a/pkg/wasm_builder/lib/src/instructions.dart
+++ b/pkg/wasm_builder/lib/src/instructions.dart
@@ -72,6 +72,15 @@
List<ValueType> get targetTypes => outputs;
}
+class Try extends Label {
+ bool hasCatch = false;
+
+ Try(List<ValueType> inputs, List<ValueType> outputs)
+ : super._(inputs, outputs);
+
+ List<ValueType> get targetTypes => outputs;
+}
+
/// A sequence of Wasm instructions.
///
/// Instructions can be added to the sequence by calling the corresponding
@@ -350,6 +359,42 @@
writeByte(0x05);
}
+ /// Emit a `try` instruction.
+ Label try_(
+ [List<ValueType> inputs = const [], List<ValueType> outputs = const []]) {
+ return _beginBlock(0x06, Try(inputs, outputs), trace: const ['try']);
+ }
+
+ /// Emit a `catch` instruction.
+ void catch_(Tag tag) {
+ assert(_topOfLabelStack is Try ||
+ _reportError("Unexpected 'catch' (not in 'try' block"));
+ final Try try_ = _topOfLabelStack as Try;
+ assert(_verifyEndOfBlock(tag.type.inputs,
+ trace: ['catch', tag], reachableAfter: try_.reachable, reindent: true));
+ try_.hasCatch = true;
+ _reachable = try_.reachable;
+ writeByte(0x07);
+ _writeTag(tag);
+ }
+
+ /// Emit a `throw` instruction.
+ void throw_(Tag tag) {
+ assert(_verifyTypes(tag.type.inputs, const [], trace: ['throw', tag]));
+ _reachable = false;
+ writeByte(0x08);
+ writeUnsigned(tag.index);
+ }
+
+ /// Emit a `rethrow` instruction.
+ void rethrow_(Label label) {
+ assert(label is Try && label.hasCatch);
+ assert(_verifyTypes(const [], const [], trace: ['rethrow', label]));
+ _reachable = false;
+ writeByte(0x09);
+ _writeLabel(label);
+ }
+
/// Emit an `end` instruction.
void end() {
assert(_verifyEndOfBlock(_topOfLabelStack.outputs,
@@ -371,6 +416,10 @@
writeUnsigned(_labelIndex(label));
}
+ void _writeTag(Tag tag) {
+ writeUnsigned(tag.index);
+ }
+
/// Emit a `br` instruction.
void br(Label label) {
assert(_verifyTypes(const [], const [],
diff --git a/pkg/wasm_builder/lib/src/module.dart b/pkg/wasm_builder/lib/src/module.dart
index 95ad8df..e015acf 100644
--- a/pkg/wasm_builder/lib/src/module.dart
+++ b/pkg/wasm_builder/lib/src/module.dart
@@ -20,6 +20,7 @@
final List<BaseFunction> functions = [];
final List<Table> tables = [];
final List<Memory> memories = [];
+ final List<Tag> tags = [];
final List<DataSegment> dataSegments = [];
final List<Global> globals = [];
final List<Export> exports = [];
@@ -124,6 +125,13 @@
return memory;
}
+ /// Add a new tag to the module.
+ Tag addTag(FunctionType type) {
+ final tag = Tag(tags.length, type);
+ tags.add(tag);
+ return tag;
+ }
+
/// Add a new data segment to the module.
///
/// Either [memory] and [offset] must be both specified or both omitted. If
@@ -213,6 +221,7 @@
FunctionSection(this).serialize(this);
TableSection(this).serialize(this);
MemorySection(this).serialize(this);
+ TagSection(this).serialize(this);
if (dataReferencedFromGlobalInitializer) {
DataCountSection(this).serialize(this);
}
@@ -392,6 +401,23 @@
}
}
+/// A tag in a module.
+class Tag implements Serializable {
+ final int index;
+ final FunctionType type;
+
+ Tag(this.index, this.type);
+
+ @override
+ void serialize(Serializer s) {
+ // 0 byte for exception.
+ s.writeByte(0x00);
+ s.write(type);
+ }
+
+ String toString() => "#$index";
+}
+
/// A data segment in a module.
class DataSegment implements Serializable {
final int index;
@@ -642,6 +668,21 @@
}
}
+class TagSection extends Section {
+ TagSection(Module module) : super(module);
+
+ @override
+ int get id => 13;
+
+ @override
+ bool get isNotEmpty => module.tags.isNotEmpty;
+
+ @override
+ void serializeContents() {
+ writeList(module.tags);
+ }
+}
+
class GlobalSection extends Section {
GlobalSection(Module module) : super(module);
diff --git a/pkg/wasm_builder/lib/wasm_builder.dart b/pkg/wasm_builder/lib/wasm_builder.dart
index 8bf1b98..7dfeb68 100644
--- a/pkg/wasm_builder/lib/wasm_builder.dart
+++ b/pkg/wasm_builder/lib/wasm_builder.dart
@@ -14,7 +14,8 @@
Local,
Memory,
Module,
- Table;
+ Table,
+ Tag;
export 'src/types.dart'
show
ArrayType,
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index f573579..3d2a0f0 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -658,7 +658,8 @@
auto initialize_callback = Isolate::InitializeCallback();
if (initialize_callback == nullptr) {
FailedSpawn(
- "Lightweight isolate spawn is not supported by this Dart embedder\n");
+ "Lightweight isolate spawn is not supported by this Dart embedder\n",
+ /*has_current_isolate=*/false);
return;
}
diff --git a/runtime/tests/vm/dart/regress_flutter98967_test.dart b/runtime/tests/vm/dart/regress_flutter98967_test.dart
new file mode 100644
index 0000000..97feb2d
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_flutter98967_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for https://github.com/flutter/flutter/issues/98967.
+// Verifies that compiler doesn't generate wrong code for comparison of ints
+// due to a late change in the representation of EqualityCompare inputs.
+
+import 'package:expect/expect.dart';
+
+class C {
+ int? val;
+
+ @pragma('vm:never-inline')
+ void testImpl(bool Function(int) compare) {
+ for (var i = 0; i < 2; i++) {
+ Expect.equals(false, compare(i));
+ val = i;
+ Expect.equals(true, compare(i));
+ }
+
+ final mint0 = int.parse("7fffffffffffffff", radix: 16);
+ final mint1 = int.parse("7fffffffffffffff", radix: 16);
+ if (mint0 != mint1) throw 'This is the same mint value';
+
+ Expect.equals(false, compare(mint0));
+ val = mint0;
+ Expect.equals(true, compare(mint0));
+ Expect.equals(true, compare(mint1),
+ 'expected two different mints with the same value compare equal');
+ }
+
+ @pragma('vm:never-inline')
+ static void blackhole(void Function() f) {
+ f();
+ }
+
+ void test() {
+ return testImpl((v) {
+ // Note: need multiple context levels in the chain to delay
+ // optimizer forwarding load of [val] and subsequently
+ // clearing null_aware flag on the equality comparison.
+ // Hence the closure capturing [v] below.
+ final result = val != null ? val == v : false;
+ blackhole(() => v);
+ return result;
+ });
+ }
+}
+
+void main() {
+ C().test();
+}
diff --git a/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart b/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart
index c9c2ae6..72c8793e 100644
--- a/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart
+++ b/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart
@@ -8,7 +8,6 @@
import 'dart:io' show Platform;
import 'dart:isolate' show Isolate, ReceivePort;
-import 'dart:typed_data' show Uint8List;
import "package:expect/expect.dart";
import 'package:front_end/src/api_unstable/vm.dart'
@@ -33,7 +32,7 @@
};
final Component component =
(await kernelForProgram(sourceUri, options))!.component!;
- final kernelBlob = writeComponentToBytes(component) as Uint8List;
+ final kernelBlob = writeComponentToBytes(component);
final kernelBlobUri =
(Isolate.current as dynamic).createUriForKernelBlob(kernelBlob);
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index 2c82c13..93c832a 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -397,6 +397,17 @@
// after this point.
void disallow_licm() { licm_allowed_ = false; }
+ // Returns true if mismatch in input/output representations is allowed.
+ bool unmatched_representations_allowed() const {
+ return unmatched_representations_allowed_;
+ }
+
+ // After the last SelectRepresentations pass all further transformations
+ // should maintain matching input/output representations.
+ void disallow_unmatched_representations() {
+ unmatched_representations_allowed_ = false;
+ }
+
PrologueInfo prologue_info() const { return prologue_info_; }
// Computes the loop hierarchy of the flow graph on demand.
@@ -623,6 +634,7 @@
ConstantInstr* constant_dead_;
bool licm_allowed_;
+ bool unmatched_representations_allowed_ = true;
const PrologueInfo prologue_info_;
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 8991d8d..256e184 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -3460,7 +3460,11 @@
flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
return replacement;
} else {
- if (!left_type->is_nullable() && !right_type->is_nullable()) {
+ // Null-aware EqualityCompare takes boxed inputs, so make sure
+ // unmatched representations are still allowed when converting
+ // EqualityCompare to the unboxed instruction.
+ if (!left_type->is_nullable() && !right_type->is_nullable() &&
+ flow_graph->unmatched_representations_allowed()) {
set_null_aware(false);
}
}
diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc
index 30502a4..d29658c 100644
--- a/runtime/vm/compiler/backend/il_test.cc
+++ b/runtime/vm/compiler/backend/il_test.cc
@@ -390,6 +390,49 @@
}
}
+static void TestNullAwareEqualityCompareCanonicalization(
+ Thread* thread,
+ bool allow_representation_change) {
+ using compiler::BlockBuilder;
+
+ CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
+
+ FlowGraphBuilderHelper H;
+
+ auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
+
+ EqualityCompareInstr* compare = nullptr;
+ {
+ BlockBuilder builder(H.flow_graph(), normal_entry);
+ Definition* v0 =
+ builder.AddParameter(0, 0, /*with_frame=*/true, kUnboxedInt64);
+ Definition* v1 =
+ builder.AddParameter(1, 1, /*with_frame=*/true, kUnboxedInt64);
+ Definition* box0 = builder.AddDefinition(new BoxInt64Instr(new Value(v0)));
+ Definition* box1 = builder.AddDefinition(new BoxInt64Instr(new Value(v1)));
+
+ compare = builder.AddDefinition(new EqualityCompareInstr(
+ InstructionSource(), Token::kEQ, new Value(box0), new Value(box1),
+ kMintCid, S.GetNextDeoptId(), /*null_aware=*/true));
+ builder.AddReturn(new Value(compare));
+ }
+
+ H.FinishGraph();
+
+ if (!allow_representation_change) {
+ H.flow_graph()->disallow_unmatched_representations();
+ }
+
+ H.flow_graph()->Canonicalize();
+
+ EXPECT(compare->is_null_aware() == !allow_representation_change);
+}
+
+ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_EqualityCompare) {
+ TestNullAwareEqualityCompareCanonicalization(thread, true);
+ TestNullAwareEqualityCompareCanonicalization(thread, false);
+}
+
static void WriteCidRangeVectorTo(const CidRangeVector& ranges,
BaseTextBuffer* buffer) {
if (ranges.is_empty()) {
diff --git a/runtime/vm/compiler/compiler_pass.cc b/runtime/vm/compiler/compiler_pass.cc
index 1faa8d3..33079e7 100644
--- a/runtime/vm/compiler/compiler_pass.cc
+++ b/runtime/vm/compiler/compiler_pass.cc
@@ -309,7 +309,7 @@
INVOKE_PASS(ConstantPropagation);
INVOKE_PASS(TypePropagation);
INVOKE_PASS(WidenSmiToInt32);
- INVOKE_PASS(SelectRepresentations);
+ INVOKE_PASS(SelectRepresentations_Final);
INVOKE_PASS(TypePropagation);
INVOKE_PASS(TryCatchOptimization);
INVOKE_PASS(EliminateEnvironments);
@@ -380,7 +380,7 @@
INVOKE_PASS(EliminateDeadPhis);
INVOKE_PASS(DCE);
INVOKE_PASS(TypePropagation);
- INVOKE_PASS(SelectRepresentations);
+ INVOKE_PASS(SelectRepresentations_Final);
INVOKE_PASS(Canonicalize);
INVOKE_PASS(UseTableDispatch);
INVOKE_PASS(EliminateStackOverflowChecks);
@@ -469,6 +469,13 @@
flow_graph->SelectRepresentations();
});
+COMPILER_PASS(SelectRepresentations_Final, {
+ // Final selection of representations. After this pass
+ // representations of inputs/outputs should match.
+ flow_graph->SelectRepresentations();
+ flow_graph->disallow_unmatched_representations();
+});
+
COMPILER_PASS(UseTableDispatch, {
state->call_specializer->ReplaceInstanceCallsWithDispatchTableCalls();
});
diff --git a/runtime/vm/compiler/compiler_pass.h b/runtime/vm/compiler/compiler_pass.h
index 01a7194..bac28af 100644
--- a/runtime/vm/compiler/compiler_pass.h
+++ b/runtime/vm/compiler/compiler_pass.h
@@ -46,6 +46,7 @@
V(RangeAnalysis) \
V(ReorderBlocks) \
V(SelectRepresentations) \
+ V(SelectRepresentations_Final) \
V(SetOuterInliningId) \
V(TryCatchOptimization) \
V(TryOptimizePatterns) \
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 48fbd5f..6c7c909 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1704,21 +1704,23 @@
intptr_t growth_in_pages,
const char* reason) {
// Save final threshold compared before growing.
- hard_gc_threshold_in_words_ =
+ intptr_t threshold =
after.CombinedUsedInWords() + (kOldPageSizeInWords * growth_in_pages);
+#if defined(TARGET_ARCH_IA32)
+ // No concurrent marking.
+ soft_gc_threshold_in_words_ = threshold;
+ hard_gc_threshold_in_words_ = threshold;
+#else
// Start concurrent marking when old-space has less than half of new-space
// available or less than 5% available.
-#if defined(TARGET_ARCH_IA32)
- const intptr_t headroom = 0; // No concurrent marking.
-#else
// Note that heap_ can be null in some unit tests.
const intptr_t new_space =
heap_ == nullptr ? 0 : heap_->new_space()->CapacityInWords();
- const intptr_t headroom =
- Utils::Maximum(new_space / 2, hard_gc_threshold_in_words_ / 20);
+ const intptr_t headroom = Utils::Maximum(new_space / 2, threshold / 20);
+ soft_gc_threshold_in_words_ = threshold;
+ hard_gc_threshold_in_words_ = threshold + headroom;
#endif
- soft_gc_threshold_in_words_ = hard_gc_threshold_in_words_ - headroom;
// Set a tight idle threshold.
idle_gc_threshold_in_words_ =
diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc
index 2b8afac..0a53fbb 100644
--- a/runtime/vm/isolate_test.cc
+++ b/runtime/vm/isolate_test.cc
@@ -22,11 +22,13 @@
// Test to ensure that an exception is thrown if no isolate creation
// callback has been set by the embedder when an isolate is spawned.
-TEST_CASE(IsolateSpawn) {
- const char* kScriptChars =
+void IsolateSpawn(const char* platform_script_value) {
+ const char* kScriptChars = OS::SCreate(
+ nullptr,
"import 'dart:isolate';\n"
// Ignores printed lines.
"var _nullPrintClosure = (String line) {};\n"
+ "var _platformScript = () => Uri.parse(\"%s\");\n"
"void entry(message) {}\n"
"void testMain() {\n"
" Isolate.spawn(entry, null);\n"
@@ -35,7 +37,8 @@
" var rp = RawReceivePort();\n"
" rp.sendPort.send(null);\n"
" rp.handler = (_) { rp.close(); };\n"
- "}\n";
+ "}\n",
+ platform_script_value);
Dart_Handle test_lib = TestCase::LoadTestScript(kScriptChars, NULL);
@@ -47,9 +50,19 @@
Dart_Handle internal_lib = Dart_LookupLibrary(url);
EXPECT_VALID(internal_lib);
Dart_Handle print = Dart_GetField(test_lib, NewString("_nullPrintClosure"));
+ EXPECT_VALID(print);
Dart_Handle result =
Dart_SetField(internal_lib, NewString("_printClosure"), print);
+ EXPECT_VALID(result);
+ Dart_Handle platform_script =
+ Dart_GetField(test_lib, NewString("_platformScript"));
+ EXPECT_VALID(platform_script);
+ Dart_Handle vmlibraryhooks_class =
+ Dart_GetClass(internal_lib, NewString("VMLibraryHooks"));
+ EXPECT_VALID(vmlibraryhooks_class);
+ result = Dart_SetField(vmlibraryhooks_class, NewString("platformScript"),
+ platform_script);
EXPECT_VALID(result);
// Setup the 'scheduleImmediate' closure.
@@ -72,12 +85,22 @@
EXPECT_VALID(result);
// Run until all ports to isolate are closed.
result = Dart_RunLoop();
- EXPECT_ERROR(result, "Unsupported operation: Isolate.spawn");
+ EXPECT_ERROR(
+ result,
+ "Lightweight isolate spawn is not supported by this Dart embedder");
EXPECT(Dart_ErrorHasException(result));
Dart_Handle exception_result = Dart_ErrorGetException(result);
EXPECT_VALID(exception_result);
}
+TEST_CASE(IsolateSpawn_FileUri) {
+ IsolateSpawn("file:/a.dart");
+}
+
+TEST_CASE(IsolateSpawn_PackageUri) {
+ IsolateSpawn("package:/a.dart");
+}
+
class InterruptChecker : public ThreadPool::Task {
public:
static const intptr_t kTaskCount;
diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart
index 34df646..4e7b164 100644
--- a/sdk/lib/_internal/vm/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart
@@ -366,7 +366,11 @@
throw new UnsupportedError("Isolate.spawn");
}
if (script.isScheme("package")) {
- script = await Isolate.resolvePackageUri(script);
+ if (Isolate._packageSupported()) {
+ // resolving script uri is not really neccessary, but can be useful
+ // for better failed-to-lookup-function-in-a-script spawn errors.
+ script = await Isolate.resolvePackageUri(script);
+ }
}
final RawReceivePort readyPort =
diff --git a/sdk/lib/_internal/wasm/lib/stack_trace_patch.dart b/sdk/lib/_internal/wasm/lib/stack_trace_patch.dart
new file mode 100644
index 0000000..4e43fa0
--- /dev/null
+++ b/sdk/lib/_internal/wasm/lib/stack_trace_patch.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+@pragma("wasm:import", "dart2wasm.getCurrentStackTrace")
+external String _getCurrentStackTrace();
+
+@patch
+class StackTrace {
+ @patch
+ @pragma("wasm:entry-point")
+ static StackTrace get current {
+ return _StringStackTrace(_getCurrentStackTrace());
+ }
+}
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index 4db66c7..619c1c0 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -204,6 +204,7 @@
"_internal/vm/lib/null_patch.dart",
"_internal/vm/lib/map_patch.dart",
"_internal/wasm/lib/object_patch.dart",
+ "_internal/wasm/lib/stack_trace_patch.dart",
"_internal/wasm/lib/string_buffer_patch.dart",
"_internal/wasm/lib/string_patch.dart",
"_internal/wasm/lib/type.dart"
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index 1730d5d..055f5e8 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -196,6 +196,7 @@
- _internal/vm/lib/null_patch.dart
- _internal/vm/lib/map_patch.dart
- _internal/wasm/lib/object_patch.dart
+ - _internal/wasm/lib/stack_trace_patch.dart
- _internal/wasm/lib/string_buffer_patch.dart
- _internal/wasm/lib/string_patch.dart
- _internal/wasm/lib/type.dart
diff --git a/tools/VERSION b/tools/VERSION
index 4038bc6..827ebec 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 148
+PRERELEASE 149
PRERELEASE_PATCH 0
\ No newline at end of file