Version 2.15.0-185.0.dev
Merge commit 'a242b4d6cc56de2175a8b344e6495ae523fcd4ee' into 'dev'
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index 988f96a..4877ec5 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -52,7 +52,19 @@
var pivotClass = _pivotClass;
if (pivotClass != null) {
_createSuperItem(pivotClass, null);
+ var superLength = _items.length;
await _createSubclasses(_items[0], 0, pivotClass);
+
+ // sort subclasses only
+ if (_items.length > superLength + 1) {
+ var subList = _items.sublist(superLength);
+ subList
+ .sort((a, b) => a.classElement.name.compareTo(b.classElement.name));
+ for (var i = 0; i < subList.length; i++) {
+ _items[i + superLength] = subList[i];
+ }
+ }
+
return _items;
}
return null;
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index 2cc6f9f..e0fef5d 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -431,6 +431,116 @@
]);
}
+ Future<void> test_class_order() async {
+ addTestFile('''
+class A {}
+class D extends A {}
+class C extends A {}
+class B extends A {}
+class G extends B {}
+class F extends B {}
+class E extends A {}
+''');
+ var items = await _getTypeHierarchy('A {}');
+ expect(_toJson(items), [
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'A',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 1,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': [2, 3, 4, 5]
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'Object',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'B',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 0,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': [6, 7]
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'C',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 0,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'D',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 0,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'E',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 0,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'F',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 4,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ },
+ {
+ 'classElement': {
+ 'kind': 'CLASS',
+ 'name': 'G',
+ 'location': anything,
+ 'flags': 0
+ },
+ 'superclass': 4,
+ 'interfaces': [],
+ 'mixins': [],
+ 'subclasses': []
+ }
+ ]);
+ }
+
Future<void> test_class_withTypes() async {
addTestFile('''
class MA {}
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index 66ba586..2b9767d 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -8,7 +8,7 @@
import 'package:analyzer/src/generated/utilities_dart.dart';
extension ElementExtension on Element {
- /// Return `true` if this element is an instance member.
+ /// Return `true` if this element is an instance member of a class or mixin.
///
/// Only [MethodElement]s and [PropertyAccessorElement]s are supported.
/// We intentionally exclude [ConstructorElement]s - they can only be
@@ -18,7 +18,7 @@
bool get isInstanceMember {
var this_ = this;
var enclosing = this_.enclosingElement;
- if (enclosing is ClassElement || enclosing is ExtensionElement) {
+ if (enclosing is ClassElement) {
return this_ is MethodElement && !this_.isStatic ||
this_ is PropertyAccessorElement && !this_.isStatic;
}
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 62bf4a9..66844bc 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -271,8 +271,7 @@
} else if (parent.declaredElement != null) {
final declaredElement = parent.declaredElement!;
if (element.isVisibleForOverriding &&
- (!declaredElement.isInstanceMember ||
- declaredElement.enclosingElement is ExtensionElement)) {
+ !declaredElement.isInstanceMember) {
reportInvalidVisibleForOverriding(declaredElement);
}
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index a05f61d..006dde9 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -48,7 +48,8 @@
Future<T> whenComplete(action());
- static Future<List<T>> wait<T>(Iterable<Future<T>> futures) => throw 0;
+ static Future<List<T>> wait<T>(Iterable<Future<T>> futures,
+ {void cleanUp(T successValue)?}) => throw 0;
}
abstract class FutureOr<T> {}
@@ -162,6 +163,8 @@
}
}
+abstract class IterableMixin<E> implements Iterable<E> { }
+
abstract class LinkedHashMap<K, V> implements Map<K, V> {
external factory LinkedHashMap(
{bool Function(K, K)? equals,
@@ -208,6 +211,12 @@
throw 0;
}
}
+
+abstract class ListMixin<E> implements List<E> { }
+
+abstract class MapMixin<K, V> implements Map<K, V> { }
+
+abstract class SetMixin<E> implements Set<E> { }
''',
)
]);
@@ -234,6 +243,10 @@
const JsonCodec();
String encode(Object? value, {Object? toEncodable(dynamic object)?}) => '';
}
+
+abstract class StringConversionSink { }
+
+abstract class StringConversionSinkMixin implements StringConversionSink { }
''',
)
],
@@ -290,7 +303,12 @@
typedef Comparator<T> = int Function(T a, T b);
-class DateTime extends Object {}
+class DateTime extends Object {
+ external DateTime._now();
+ DateTime.now() : this._now();
+ external bool isBefore(DateTime other);
+ external int get millisecondsSinceEpoch;
+}
class Deprecated extends Object {
final String expires;
@@ -360,6 +378,8 @@
bool get isEven => false;
bool get isNegative;
+ bool get isOdd;
+ int get sign;
int operator &(int other);
int operator -();
@@ -371,8 +391,10 @@
int operator ~();
int abs();
+ int ceil();
int gcd(int other);
String toString();
+ int truncate();
external static int parse(String source,
{int? radix, @deprecated int onError(String source)?});
@@ -389,7 +411,11 @@
Iterator<E> get iterator;
int get length;
- bool contains(Object element);
+ const Iterable();
+
+ const factory Iterable.empty() => EmptyIterable<E>();
+
+ bool contains(Object? element);
Iterable<T> expand<T>(Iterable<T> f(E element));
@@ -410,6 +436,7 @@
Set<E> toSet();
Iterable<E> where(bool test(E element));
+ Iterable<T> whereType<T>();
}
abstract class Iterator<E> {
@@ -436,6 +463,7 @@
Map<int, E> asMap() {}
void clear() {}
int indexOf(Object element);
+ bool remove(Object? value);
E removeLast() {}
noSuchMethod(Invocation invocation) => null;
@@ -463,8 +491,11 @@
V? operator [](Object? key);
void operator []=(K key, V value);
+ void addAll(Map<K, V> other);
Map<RK, RV> cast<RK, RV>();
bool containsKey(Object? key);
+ void forEach(void action(K key, V value));
+ V putIfAbsent(K key, V ifAbsent());
}
class Null extends Object {
@@ -541,7 +572,7 @@
}
abstract class RegExp implements Pattern {
- external factory RegExp(String source);
+ external factory RegExp(String source, {bool unicode = false});
}
abstract class Set<E> implements Iterable<E> {
@@ -895,6 +926,7 @@
"iframe");
String src;
+ set srcdoc(String? value) native;
}
class OptionElement extends HtmlElement {
@@ -997,6 +1029,10 @@
class Symbol {}
+class EmptyIterable<E> implements Iterable<E> {
+ const EmptyIterable();
+}
+
class ExternalName {
final String name;
const ExternalName(this.name);
diff --git a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
index 902e2e7..73f39f6 100644
--- a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
@@ -1773,6 +1773,32 @@
assertType(invocation, 'int');
}
+ test_instance_getter_asSetter() async {
+ await assertErrorsInCode('''
+extension E1 on int {
+ int get foo => 0;
+}
+
+extension E2 on int {
+ int get foo => 0;
+ void f() {
+ foo = 0;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 104, 3),
+ ]);
+ assertAssignment(
+ findNode.assignment('foo = 0'),
+ readElement: null,
+ readType: null,
+ writeElement: findElement.getter('foo', of: 'E2'),
+ writeType: 'dynamic',
+ operatorElement: null,
+ type: 'int',
+ );
+ }
+
test_instance_getter_fromInstance() async {
await assertNoErrorsInCode('''
class C {
@@ -1978,6 +2004,28 @@
assertElement(prefix, findElement.method('unary-', of: 'E'));
}
+ test_instance_setter_asGetter() async {
+ await assertErrorsInCode('''
+extension E1 on int {
+ set foo(int _) {}
+}
+
+extension E2 on int {
+ set foo(int _) {}
+ void f() {
+ foo;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 104, 3),
+ ]);
+ assertSimpleIdentifier(
+ findNode.simple('foo;'),
+ element: null,
+ type: 'dynamic',
+ );
+ }
+
test_instance_setter_fromInstance() async {
await assertNoErrorsInCode('''
class C {
diff --git a/pkg/analyzer/test/verify_diagnostics_test.dart b/pkg/analyzer/test/verify_diagnostics_test.dart
index d8d7076..f1554cf 100644
--- a/pkg/analyzer/test/verify_diagnostics_test.dart
+++ b/pkg/analyzer/test/verify_diagnostics_test.dart
@@ -8,12 +8,12 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:analyzer/src/dart/ast/token.dart';
import 'package:path/path.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../tool/diagnostics/generate.dart';
+import '../tool/messages/error_code_documentation_info.dart';
import 'src/dart/resolution/context_collection_resolution.dart';
main() {
@@ -111,18 +111,6 @@
'PubspecWarningCode.UNNECESSARY_DEV_DEPENDENCY',
];
- /// The prefix used on directive lines to specify the experiments that should
- /// be enabled for a snippet.
- static const String experimentsPrefix = '%experiments=';
-
- /// The prefix used on directive lines to specify the language version for
- /// the snippet.
- static const String languagePrefix = '%language=';
-
- /// The prefix used on directive lines to indicate the uri of an auxiliary
- /// file that is needed for testing purposes.
- static const String uriDirectivePrefix = '%uri="';
-
/// The absolute paths of the files containing the declarations of the error
/// codes.
final List<CodePath> codePaths;
@@ -174,30 +162,6 @@
return variable.name.name;
}
- /// Extract documentation from the given [field] declaration.
- List<String>? _extractDoc(FieldDeclaration field) {
- var comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
- if (comments == null) {
- return null;
- }
- List<String> docs = [];
- while (comments != null) {
- String lexeme = comments.lexeme;
- if (lexeme.startsWith('// TODO')) {
- break;
- } else if (lexeme.startsWith('// ')) {
- docs.add(lexeme.substring(3));
- } else if (lexeme == '//') {
- docs.add('');
- }
- comments = comments.next as CommentToken?;
- }
- if (docs.isEmpty) {
- return null;
- }
- return docs;
- }
-
_SnippetData _extractSnippetData(
String snippet,
bool errorRequired,
@@ -232,52 +196,32 @@
languageVersion);
}
- /// Extract the snippets of Dart code between the start (inclusive) and end
- /// (exclusive) indexes.
+ /// Extract the snippets of Dart code from [documentationParts] that are
+ /// tagged as belonging to the given [blockSection].
List<_SnippetData> _extractSnippets(
- List<String> lines, int start, int end, bool errorRequired) {
+ List<ErrorCodeDocumentationPart> documentationParts,
+ BlockSection blockSection) {
var snippets = <_SnippetData>[];
var auxiliaryFiles = <String, String>{};
- List<String>? experiments;
- String? languageVersion;
- var currentStart = -1;
- for (var i = start; i < end; i++) {
- var line = lines[i];
- if (line == '```') {
- if (currentStart < 0) {
- _reportProblem('Snippet without file type on line $i.');
- return snippets;
+ for (var documentationPart in documentationParts) {
+ if (documentationPart is ErrorCodeDocumentationBlock) {
+ if (documentationPart.containingSection != blockSection) {
+ continue;
}
- var secondLine = lines[currentStart + 1];
- if (secondLine.startsWith(uriDirectivePrefix)) {
- var name = secondLine.substring(
- uriDirectivePrefix.length, secondLine.length - 1);
- var content = lines.sublist(currentStart + 2, i).join('\n');
- auxiliaryFiles[name] = content;
- } else if (lines[currentStart] == '```dart') {
- if (secondLine.startsWith(experimentsPrefix)) {
- experiments = secondLine
- .substring(experimentsPrefix.length)
- .split(',')
- .map((e) => e.trim())
- .toList();
- currentStart++;
- } else if (secondLine.startsWith(languagePrefix)) {
- languageVersion = secondLine.substring(languagePrefix.length);
- currentStart++;
+ var uri = documentationPart.uri;
+ if (uri != null) {
+ auxiliaryFiles[uri] = documentationPart.text;
+ } else {
+ if (documentationPart.fileType == 'dart') {
+ snippets.add(_extractSnippetData(
+ documentationPart.text,
+ blockSection == BlockSection.examples,
+ auxiliaryFiles,
+ documentationPart.experiments,
+ documentationPart.languageVersion));
}
- var content = lines.sublist(currentStart + 1, i).join('\n');
- snippets.add(_extractSnippetData(content, errorRequired,
- auxiliaryFiles, experiments ?? [], languageVersion));
auxiliaryFiles = <String, String>{};
}
- currentStart = -1;
- } else if (line.startsWith('```')) {
- if (currentStart >= 0) {
- _reportProblem('Snippet before line $i was not closed.');
- return snippets;
- }
- currentStart = i;
}
}
return snippets;
@@ -329,7 +273,7 @@
String className = declaration.name.name;
for (ClassMember member in declaration.members) {
if (member is FieldDeclaration) {
- var docs = _extractDoc(member);
+ var docs = parseErrorCodeDocumentation(member);
if (docs != null) {
VariableDeclaration variable = member.fields.variables[0];
codeName = _extractCodeName(variable);
@@ -342,11 +286,8 @@
}
hasWrittenVariableName = false;
- int exampleStart = docs.indexOf('#### Examples');
- int fixesStart = docs.indexOf('#### Common fixes');
-
List<_SnippetData> exampleSnippets =
- _extractSnippets(docs, exampleStart + 1, fixesStart, true);
+ _extractSnippets(docs, BlockSection.examples);
_SnippetData? firstExample;
if (exampleSnippets.isEmpty) {
_reportProblem('No example.');
@@ -358,7 +299,7 @@
}
List<_SnippetData> fixesSnippets =
- _extractSnippets(docs, fixesStart + 1, docs.length, false);
+ _extractSnippets(docs, BlockSection.commonFixes);
for (int i = 0; i < fixesSnippets.length; i++) {
_SnippetData snippet = fixesSnippets[i];
if (firstExample != null) {
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
index 9ed18ff..7d0b2f9 100644
--- a/pkg/analyzer/tool/diagnostics/generate.dart
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -9,10 +9,11 @@
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer_utilities/package_root.dart' as package_root;
import 'package:path/src/context.dart';
+import '../messages/error_code_documentation_info.dart';
+
/// Generate the file `diagnostics.md` based on the documentation associated
/// with the declarations of the error codes.
void main() async {
@@ -95,8 +96,8 @@
/// The messages associated with the diagnostic.
List<String> messages;
- /// The lines of documentation associated with the diagnostic.
- List<String>? documentation;
+ /// The documentation text associated with the diagnostic.
+ String? documentation;
/// Initialize a newly created information holder with the given [name] and
/// [message].
@@ -123,9 +124,7 @@
}
}
sink.writeln();
- for (String line in documentation!) {
- sink.writeln(line);
- }
+ sink.writeln(documentation!);
}
/// Return a version of the [text] in which characters that have special
@@ -244,43 +243,15 @@
}
/// Extract documentation from the given [field] declaration.
- List<String>? _extractDoc(FieldDeclaration field) {
- var comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
- if (comments == null) {
- return null;
+ String _extractDoc(FieldDeclaration field) {
+ var parsedComment = parseErrorCodeDocumentation(field);
+ if (parsedComment == null) {
+ return '';
}
- List<String> docs = [];
- bool inDartCodeBlock = false;
- while (comments != null) {
- String lexeme = comments.lexeme;
- if (lexeme.startsWith('// TODO')) {
- break;
- } else if (lexeme.startsWith('// %')) {
- // Ignore lines containing directives for testing support.
- } else if (lexeme.startsWith('// ')) {
- String trimmedLine = lexeme.substring(3);
- if (trimmedLine == '```dart') {
- inDartCodeBlock = true;
- docs.add('{% prettify dart tag=pre+code %}');
- } else if (trimmedLine == '```') {
- if (inDartCodeBlock) {
- docs.add('{% endprettify %}');
- inDartCodeBlock = false;
- } else {
- docs.add(trimmedLine);
- }
- } else {
- docs.add(trimmedLine);
- }
- } else if (lexeme == '//') {
- docs.add('');
- }
- comments = comments.next as CommentToken?;
- }
- if (docs.isEmpty) {
- return null;
- }
- return docs;
+ return [
+ for (var documentationPart in parsedComment)
+ documentationPart.formatForDocumentation()
+ ].join('\n');
}
/// Extract documentation from the file that was parsed to produce the given
@@ -301,7 +272,7 @@
variable.initializer!, generatedResult);
if (info != null) {
var docs = _extractDoc(member);
- if (docs != null) {
+ if (docs.isNotEmpty) {
if (info.documentation != null) {
throw StateError(
'Documentation defined multiple times for ${info.name}');
diff --git a/pkg/analyzer/tool/messages/error_code_documentation_info.dart b/pkg/analyzer/tool/messages/error_code_documentation_info.dart
new file mode 100644
index 0000000..37a4a76
--- /dev/null
+++ b/pkg/analyzer/tool/messages/error_code_documentation_info.dart
@@ -0,0 +1,247 @@
+// Copyright (c) 2021, 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:convert';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/token.dart';
+
+/// Extracts comments from the declaration of [field] and interprets them as a
+/// list of [ErrorCodeDocumentationPart] objects. These objects represent
+/// user-publishable documentation about the error code, along with code blocks
+/// illustrating when the error occurs and how to fix it.
+List<ErrorCodeDocumentationPart>? parseErrorCodeDocumentation(
+ FieldDeclaration field) {
+ var comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
+ if (comments == null) {
+ return null;
+ }
+ var className = (field.parent as ClassDeclaration).name.name;
+ var errorName = field.fields.variables.single.name.name;
+ var commentLines = <String>[];
+ while (comments != null) {
+ String lexeme = comments.lexeme;
+ if (lexeme.startsWith('// ')) {
+ commentLines.add(lexeme.substring(3));
+ } else if (lexeme == '//') {
+ commentLines.add('');
+ }
+ comments = comments.next as CommentToken?;
+ }
+ if (commentLines.isEmpty) {
+ return null;
+ }
+ var parser =
+ _ErrorCodeDocumentationParser('$className.$errorName', commentLines);
+ parser.parse();
+ return parser.result;
+}
+
+/// Enum representing the different documentation sections in which an
+/// [ErrorCodeDocumentationBlock] might appear.
+enum BlockSection {
+ // The "Examples" section, where we give examples of code that generates the
+ // error.
+ examples,
+
+ // The "Common fixes" section, where we give examples of code that doesn't
+ // generate the error.
+ commonFixes,
+}
+
+/// An [ErrorCodeDocumentationPart] containing a block of code.
+class ErrorCodeDocumentationBlock extends ErrorCodeDocumentationPart {
+ /// The code itself.
+ final String text;
+
+ /// The section this block is contained in.
+ final BlockSection containingSection;
+
+ /// A list of the experiments that need to be enabled for this code to behave
+ /// as expected (if any).
+ final List<String> experiments;
+
+ /// The file type of this code block (e.g. `dart` or `yaml`).
+ final String fileType;
+
+ /// The language version that must be active for this code to behave as
+ /// expected (if any).
+ final String? languageVersion;
+
+ /// If this code is an auxiliary file that supports other blocks, the URI of
+ /// the file.
+ final String? uri;
+
+ ErrorCodeDocumentationBlock(this.text,
+ {required this.containingSection,
+ this.experiments = const [],
+ required this.fileType,
+ this.languageVersion,
+ this.uri});
+
+ @override
+ String formatForDocumentation() => fileType == 'dart'
+ ? ['{% prettify dart tag=pre+code %}', text, '{% endprettify %}']
+ .join('\n')
+ : ['```$fileType', text, '```'].join('\n');
+}
+
+/// A portion of an error code's documentation. This could be free form
+/// markdown text ([ErrorCodeDocumentationText]) or a code block
+/// ([ErrorCodeDocumentationBlock]).
+abstract class ErrorCodeDocumentationPart {
+ /// Formats this documentation part as text suitable for inclusion in the
+ /// analyzer's `diagnostics.md` file.
+ String formatForDocumentation();
+}
+
+/// An [ErrorCodeDocumentationPart] containing free form markdown text.
+class ErrorCodeDocumentationText extends ErrorCodeDocumentationPart {
+ /// The text, in markdown format.
+ final String text;
+
+ ErrorCodeDocumentationText(this.text);
+
+ @override
+ String formatForDocumentation() => text;
+}
+
+class _ErrorCodeDocumentationParser {
+ /// The prefix used on directive lines to specify the experiments that should
+ /// be enabled for a snippet.
+ static const String experimentsPrefix = '%experiments=';
+
+ /// The prefix used on directive lines to specify the language version for
+ /// the snippet.
+ static const String languagePrefix = '%language=';
+
+ /// The prefix used on directive lines to indicate the uri of an auxiliary
+ /// file that is needed for testing purposes.
+ static const String uriDirectivePrefix = '%uri="';
+
+ final String errorCode;
+
+ final List<String> commentLines;
+
+ final List<ErrorCodeDocumentationPart> result = [];
+
+ int currentLineNumber = 0;
+
+ String? currentSection;
+
+ _ErrorCodeDocumentationParser(this.errorCode, this.commentLines);
+
+ bool get done => currentLineNumber >= commentLines.length;
+
+ String get line => commentLines[currentLineNumber];
+
+ BlockSection computeCurrentBlockSection() {
+ switch (currentSection) {
+ case '#### Example':
+ case '#### Examples':
+ return BlockSection.examples;
+ case '#### Common fixes':
+ return BlockSection.commonFixes;
+ case null:
+ problem('Code block before section header');
+ default:
+ problem('Code block in invalid section ${json.encode(currentSection)}');
+ }
+ }
+
+ void parse() {
+ var textLines = <String>[];
+
+ void flushText() {
+ if (textLines.isNotEmpty) {
+ result.add(ErrorCodeDocumentationText(textLines.join('\n')));
+ textLines = [];
+ }
+ }
+
+ while (!done) {
+ if (line.startsWith('TODO')) {
+ // Everything after the "TODO" is ignored.
+ break;
+ } else if (line.startsWith('%')) {
+ problem('% directive outside code block');
+ } else if (line.startsWith('```')) {
+ flushText();
+ processCodeBlock();
+ } else {
+ if (line.startsWith('#') && !line.startsWith('#####')) {
+ currentSection = line;
+ }
+ textLines.add(line);
+ currentLineNumber++;
+ }
+ }
+ flushText();
+ }
+
+ Never problem(String explanation) {
+ throw 'In documentation for $errorCode, at line ${currentLineNumber + 1}, '
+ '$explanation';
+ }
+
+ void processCodeBlock() {
+ var containingSection = computeCurrentBlockSection();
+ var codeLines = <String>[];
+ String? languageVersion;
+ String? uri;
+ List<String>? experiments;
+ assert(line.startsWith('```'));
+ var fileType = line.substring(3);
+ if (fileType.isEmpty) {
+ problem('Code blocks should have a file type, e.g. "```dart"');
+ }
+ ++currentLineNumber;
+ while (true) {
+ if (done) {
+ problem('Unterminated code block');
+ } else if (line.startsWith('```')) {
+ if (line != '```') {
+ problem('Code blocks should end with "```"');
+ }
+ ++currentLineNumber;
+ result.add(ErrorCodeDocumentationBlock(codeLines.join('\n'),
+ containingSection: containingSection,
+ experiments: experiments ?? const [],
+ fileType: fileType,
+ languageVersion: languageVersion,
+ uri: uri));
+ return;
+ } else if (line.startsWith('%')) {
+ if (line.startsWith(languagePrefix)) {
+ if (languageVersion != null) {
+ problem('Multiple language version directives');
+ }
+ languageVersion = line.substring(languagePrefix.length);
+ } else if (line.startsWith(uriDirectivePrefix)) {
+ if (uri != null) {
+ problem('Multiple URI directives');
+ }
+ if (!line.endsWith('"')) {
+ problem('URI directive should be surrounded by double quotes');
+ }
+ uri = line.substring(uriDirectivePrefix.length, line.length - 1);
+ } else if (line.startsWith(experimentsPrefix)) {
+ if (experiments != null) {
+ problem('Multiple experiments directives');
+ }
+ experiments = line
+ .substring(experimentsPrefix.length)
+ .split(',')
+ .map((e) => e.trim())
+ .toList();
+ } else {
+ problem('Unrecognized directive ${json.encode(line)}');
+ }
+ } else {
+ codeLines.add(line);
+ }
+ ++currentLineNumber;
+ }
+ }
+}
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 09b4aea..bc87f29 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -267,17 +267,19 @@
}
GlobalTypeInferenceResults globalTypeInferenceResults =
performGlobalTypeInference(closedWorldAndIndices.closedWorld);
+ var indices = closedWorldAndIndices.indices;
if (options.writeDataUri != null) {
if (options.noClosedWorldInData) {
serializationTask.serializeGlobalTypeInference(
- globalTypeInferenceResults, closedWorldAndIndices.indices);
+ globalTypeInferenceResults, indices);
} else {
serializationTask
.serializeGlobalTypeInferenceLegacy(globalTypeInferenceResults);
}
return;
}
- await generateJavaScriptCode(globalTypeInferenceResults);
+ await generateJavaScriptCode(globalTypeInferenceResults,
+ indices: indices);
} else if (onlyPerformCodegen) {
GlobalTypeInferenceResults globalTypeInferenceResults;
ir.Component component =
@@ -291,7 +293,8 @@
abstractValueStrategy,
component,
closedWorldAndIndices);
- await generateJavaScriptCode(globalTypeInferenceResults);
+ await generateJavaScriptCode(globalTypeInferenceResults,
+ indices: closedWorldAndIndices.indices);
} else if (options.readDataUri != null) {
// TODO(joshualitt) delete and clean up after google3 roll
var globalTypeInferenceResults =
@@ -324,7 +327,8 @@
}
void generateJavaScriptCode(
- GlobalTypeInferenceResults globalTypeInferenceResults) async {
+ GlobalTypeInferenceResults globalTypeInferenceResults,
+ {DataSourceIndices indices}) async {
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
backendStrategy.registerJClosedWorld(closedWorld);
phase = PHASE_COMPILING;
@@ -333,8 +337,8 @@
if (options.readCodegenUri != null) {
CodegenResults codegenResults =
- await serializationTask.deserializeCodegen(
- backendStrategy, globalTypeInferenceResults, codegenInputs);
+ await serializationTask.deserializeCodegen(backendStrategy,
+ globalTypeInferenceResults, codegenInputs, indices);
reporter.log('Compiling methods');
runCodegenEnqueuer(codegenResults);
} else {
@@ -344,7 +348,8 @@
codegenInputs,
backendStrategy.functionCompiler);
if (options.writeCodegenUri != null) {
- serializationTask.serializeCodegen(backendStrategy, codegenResults);
+ serializationTask.serializeCodegen(
+ backendStrategy, codegenResults, indices);
} else {
runCodegenEnqueuer(codegenResults);
}
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index b4943f1..071727d 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -353,8 +353,8 @@
// TODO(joshualitt): Investigate whether closed world indices can be shared
// with codegen.
- void serializeCodegen(
- BackendStrategy backendStrategy, CodegenResults codegenResults) {
+ void serializeCodegen(BackendStrategy backendStrategy,
+ CodegenResults codegenResults, DataSourceIndices indices) {
GlobalTypeInferenceResults globalTypeInferenceResults =
codegenResults.globalTypeInferenceResults;
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
@@ -373,7 +373,8 @@
measureSubtask('serialize codegen', () {
Uri uri = Uri.parse('${_options.writeCodegenUri}$shard');
api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(uri);
- DataSink sink = BinarySink(BinaryOutputSinkAdapter(dataOutput));
+ DataSink sink = BinarySink(BinaryOutputSinkAdapter(dataOutput),
+ importedIndices: indices);
_reporter.log('Writing data to ${uri}');
sink.registerEntityWriter(entityWriter);
sink.registerCodegenWriter(CodegenWriterImpl(closedWorld));
@@ -388,7 +389,8 @@
Future<CodegenResults> deserializeCodegen(
BackendStrategy backendStrategy,
GlobalTypeInferenceResults globalTypeInferenceResults,
- CodegenInputs codegenInputs) async {
+ CodegenInputs codegenInputs,
+ DataSourceIndices indices) async {
int shards = _options.codegenShards;
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
Map<MemberEntity, CodegenResult> results = {};
@@ -401,7 +403,7 @@
// TODO(36983): This code is extracted because there appeared to be a
// memory leak for large buffer held by `source`.
_deserializeCodegenInput(
- backendStrategy, closedWorld, uri, dataInput, results);
+ backendStrategy, closedWorld, uri, dataInput, indices, results);
dataInput.release();
});
}
@@ -414,9 +416,10 @@
JClosedWorld closedWorld,
Uri uri,
api.Input<List<int>> dataInput,
+ DataSourceIndices importedIndices,
Map<MemberEntity, CodegenResult> results) {
- DataSource source =
- BinarySourceImpl(dataInput.data, stringInterner: _stringInterner);
+ DataSource source = BinarySourceImpl(dataInput.data,
+ stringInterner: _stringInterner, importedIndices: importedIndices);
backendStrategy.prepareCodegenReader(source);
Map<MemberEntity, CodegenResult> codegenResults =
source.readMemberMap((MemberEntity member) {
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 4d42ff3..c045cc1 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -336,6 +336,7 @@
if (useModularAnalysis)
'${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
'${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
+ Flags.noClosedWorldInData,
'--out=${toUri(module, globalUpdatedDillId)}',
];
var result =
diff --git a/runtime/tests/concurrency/run_stress_test_shards.dart b/runtime/tests/concurrency/run_stress_test_shards.dart
index 87eb9ab..d1d1d80 100644
--- a/runtime/tests/concurrency/run_stress_test_shards.dart
+++ b/runtime/tests/concurrency/run_stress_test_shards.dart
@@ -39,10 +39,14 @@
forwardStream(process.stderr, stderr);
final int exitCode = await process.exitCode;
if (exitCode != 0) {
- final crashNr = crashCounter++;
- print('=> Running "$executable ${args.join(' ')}" failed with $exitCode');
- print('=> Possible crash $crashNr (pid: ${process.pid})');
- crashes.add(PotentialCrash('crash-$crashNr', process.pid, [executable]));
+ // Ignore normal exceptions and compile-time errors for the purpose of
+ // crashdump reporting.
+ if (exitCode != 255 && exitCode != 254) {
+ final crashNr = crashCounter++;
+ print('=> Running "$executable ${args.join(' ')}" failed with $exitCode');
+ print('=> Possible crash $crashNr (pid: ${process.pid})');
+ crashes.add(PotentialCrash('crash-$crashNr', process.pid, [executable]));
+ }
io.exitCode = 255; // Make this shard fail.
return false;
}
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 1d74f0a..85e1bcb 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -2290,9 +2290,35 @@
s->WriteUnsigned(length);
uint8_t* entry_bits = pool->untag()->entry_bits();
for (intptr_t j = 0; j < length; j++) {
- s->Write<uint8_t>(entry_bits[j]);
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
- switch (ObjectPool::TypeBits::decode(entry_bits[j])) {
+ uint8_t bits = entry_bits[j];
+ ObjectPool::EntryType type = ObjectPool::TypeBits::decode(bits);
+ if (weak && (type == ObjectPool::EntryType::kTaggedObject)) {
+ // By default, every switchable call site will put (ic_data, code)
+ // into the object pool. The [code] is initialized (at AOT
+ // compile-time) to be [StubCode::SwitchableCallMiss] or
+ // [StubCode::MegamorphicCall].
+ //
+ // In --use-bare-instruction we reduce the extra indirection via
+ // the [code] object and store instead (ic_data, entrypoint) in
+ // the object pool.
+ //
+ // Since the actual [entrypoint] is only known at AOT runtime we
+ // switch all existing entries for these stubs to entrypoints
+ // encoded as EntryType::kSwitchableCallMissEntryPoint and
+ // EntryType::kMegamorphicCallEntryPoint.
+ if (entry.raw_obj_ == StubCode::SwitchableCallMiss().ptr()) {
+ type = ObjectPool::EntryType::kSwitchableCallMissEntryPoint;
+ bits = ObjectPool::EncodeBits(type,
+ ObjectPool::Patchability::kPatchable);
+ } else if (entry.raw_obj_ == StubCode::MegamorphicCall().ptr()) {
+ type = ObjectPool::EntryType::kMegamorphicCallEntryPoint;
+ bits = ObjectPool::EncodeBits(type,
+ ObjectPool::Patchability::kPatchable);
+ }
+ }
+ s->Write<uint8_t>(bits);
+ switch (type) {
case ObjectPool::EntryType::kTaggedObject: {
if ((entry.raw_obj_ == StubCode::CallNoScopeNative().ptr()) ||
(entry.raw_obj_ == StubCode::CallAutoScopeNative().ptr())) {
@@ -2319,6 +2345,11 @@
// Write nothing. Will initialize with the lazy link entry.
break;
}
+ case ObjectPool::EntryType::kSwitchableCallMissEntryPoint:
+ case ObjectPool::EntryType::kMegamorphicCallEntryPoint:
+ // Write nothing. Entry point is initialized during
+ // snapshot deserialization.
+ break;
default:
UNREACHABLE();
}
@@ -2351,6 +2382,17 @@
void ReadFill(Deserializer* d, bool primary) {
ASSERT(!is_canonical()); // Never canonical.
fill_position_ = d->position();
+ const uint8_t immediate_bits =
+ ObjectPool::EncodeBits(ObjectPool::EntryType::kImmediate,
+ ObjectPool::Patchability::kPatchable);
+ uword switchable_call_miss_entry_point = 0;
+ uword megamorphic_call_entry_point = 0;
+ if (FLAG_use_bare_instructions) {
+ switchable_call_miss_entry_point =
+ StubCode::SwitchableCallMiss().MonomorphicEntryPoint();
+ megamorphic_call_entry_point =
+ StubCode::MegamorphicCall().MonomorphicEntryPoint();
+ }
for (intptr_t id = start_index_; id < stop_index_; id++) {
const intptr_t length = d->ReadUnsigned();
@@ -2375,6 +2417,18 @@
entry.raw_value_ = static_cast<intptr_t>(new_entry);
break;
}
+ case ObjectPool::EntryType::kSwitchableCallMissEntryPoint:
+ ASSERT(FLAG_use_bare_instructions);
+ pool->untag()->entry_bits()[j] = immediate_bits;
+ entry.raw_value_ =
+ static_cast<intptr_t>(switchable_call_miss_entry_point);
+ break;
+ case ObjectPool::EntryType::kMegamorphicCallEntryPoint:
+ ASSERT(FLAG_use_bare_instructions);
+ pool->untag()->entry_bits()[j] = immediate_bits;
+ entry.raw_value_ =
+ static_cast<intptr_t>(megamorphic_call_entry_point);
+ break;
default:
UNREACHABLE();
}
@@ -6894,6 +6948,8 @@
// Code cluster should be deserialized before Function as
// FunctionDeserializationCluster::ReadFill uses instructions table
// which is filled in CodeDeserializationCluster::ReadFill.
+ // Code cluster should also precede ObjectPool as its ReadFill uses
+ // entry points of stubs.
ADD_NON_CANONICAL_NEXT(kCodeCid)
// The function cluster should be deserialized before any closures, as
// PostLoad for closures caches the entry point found in the function.
@@ -8305,7 +8361,6 @@
ProgramDeserializationRoots roots(thread_->isolate_group()->object_store());
deserializer.Deserialize(&roots);
- PatchGlobalObjectPool();
InitializeBSS();
return ApiError::null();
@@ -8351,52 +8406,11 @@
UnitDeserializationRoots roots(unit);
deserializer.Deserialize(&roots);
- PatchGlobalObjectPool();
InitializeBSS();
return ApiError::null();
}
-void FullSnapshotReader::PatchGlobalObjectPool() {
-#if defined(DART_PRECOMPILED_RUNTIME)
- if (FLAG_use_bare_instructions) {
- // By default, every switchable call site will put (ic_data, code) into the
- // object pool. The [code] is initialized (at AOT compile-time) to be a
- // [StubCode::SwitchableCallMiss].
- //
- // In --use-bare-instruction we reduce the extra indirection via the [code]
- // object and store instead (ic_data, entrypoint) in the object pool.
- //
- // Since the actual [entrypoint] is only known at AOT runtime we switch all
- // existing UnlinkedCall entries in the object pool to be it's entrypoint.
- auto zone = thread_->zone();
- const auto& pool = ObjectPool::Handle(
- zone, ObjectPool::RawCast(
- isolate_group()->object_store()->global_object_pool()));
- auto& entry = Object::Handle(zone);
- auto& smi = Smi::Handle(zone);
- for (intptr_t i = 0; i < pool.Length(); i++) {
- if (pool.TypeAt(i) == ObjectPool::EntryType::kTaggedObject) {
- entry = pool.ObjectAt(i);
- if (entry.ptr() == StubCode::SwitchableCallMiss().ptr()) {
- smi = Smi::FromAlignedAddress(
- StubCode::SwitchableCallMiss().MonomorphicEntryPoint());
- pool.SetTypeAt(i, ObjectPool::EntryType::kImmediate,
- ObjectPool::Patchability::kPatchable);
- pool.SetObjectAt(i, smi);
- } else if (entry.ptr() == StubCode::MegamorphicCall().ptr()) {
- smi = Smi::FromAlignedAddress(
- StubCode::MegamorphicCall().MonomorphicEntryPoint());
- pool.SetTypeAt(i, ObjectPool::EntryType::kImmediate,
- ObjectPool::Patchability::kPatchable);
- pool.SetObjectAt(i, smi);
- }
- }
- }
- }
-#endif // defined(DART_PRECOMPILED_RUNTIME)
-}
-
void FullSnapshotReader::InitializeBSS() {
#if defined(DART_PRECOMPILED_RUNTIME)
// Initialize entries in the isolate portion of the BSS segment.
diff --git a/runtime/vm/app_snapshot.h b/runtime/vm/app_snapshot.h
index da96ab4..7bc3032 100644
--- a/runtime/vm/app_snapshot.h
+++ b/runtime/vm/app_snapshot.h
@@ -805,7 +805,6 @@
IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
ApiErrorPtr ConvertToApiError(char* message);
- void PatchGlobalObjectPool();
void InitializeBSS();
Snapshot::Kind kind_;
diff --git a/runtime/vm/compiler/assembler/object_pool_builder.h b/runtime/vm/compiler/assembler/object_pool_builder.h
index db77eb8..e33ded6 100644
--- a/runtime/vm/compiler/assembler/object_pool_builder.h
+++ b/runtime/vm/compiler/assembler/object_pool_builder.h
@@ -30,6 +30,14 @@
kImmediate,
kNativeFunction,
kNativeFunctionWrapper,
+
+ // Used only during AOT snapshot serialization/deserialization.
+ // Denotes kImmediate entry with
+ // - StubCode::SwitchableCallMiss().MonomorphicEntryPoint()
+ // - StubCode::MegamorphicCall().MonomorphicEntryPoint()
+ // values which become known only at run time.
+ kSwitchableCallMissEntryPoint,
+ kMegamorphicCallEntryPoint,
};
using TypeBits = BitField<uint8_t, EntryType, 0, 7>;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index c76665a..7959652 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5254,10 +5254,13 @@
return PatchableBit::decode(untag()->entry_bits()[index]);
}
+ static uint8_t EncodeBits(EntryType type, Patchability patchable) {
+ return PatchableBit::encode(patchable) | TypeBits::encode(type);
+ }
+
void SetTypeAt(intptr_t index, EntryType type, Patchability patchable) const {
ASSERT(index >= 0 && index <= Length());
- const uint8_t bits =
- PatchableBit::encode(patchable) | TypeBits::encode(type);
+ const uint8_t bits = EncodeBits(type, patchable);
StoreNonPointer(&untag()->entry_bits()[index], bits);
}
diff --git a/runtime/vm/timer.cc b/runtime/vm/timer.cc
index 4757a5e..894966f 100644
--- a/runtime/vm/timer.cc
+++ b/runtime/vm/timer.cc
@@ -5,5 +5,14 @@
#include "vm/timer.h"
#include "platform/globals.h"
#include "vm/json_stream.h"
+#include "vm/thread.h"
-namespace dart {} // namespace dart
+namespace dart {
+
+PrintTimeScope::~PrintTimeScope() {
+ timer_.Stop();
+ OS::PrintErr("%s %s\n", name_,
+ timer_.FormatElapsedHumanReadable(Thread::Current()->zone()));
+}
+
+} // namespace dart
diff --git a/runtime/vm/timer.h b/runtime/vm/timer.h
index 8f478d5..19c11a7 100644
--- a/runtime/vm/timer.h
+++ b/runtime/vm/timer.h
@@ -198,6 +198,16 @@
Timer* const timer_;
};
+class PrintTimeScope : public ValueObject {
+ public:
+ explicit PrintTimeScope(const char* name) : name_(name) { timer_.Start(); }
+ ~PrintTimeScope();
+
+ private:
+ Timer timer_;
+ const char* name_;
+};
+
} // namespace dart
#endif // RUNTIME_VM_TIMER_H_
diff --git a/tools/VERSION b/tools/VERSION
index 3ada223..8f9bd6b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 184
+PRERELEASE 185
PRERELEASE_PATCH 0
\ No newline at end of file