Version 2.14.0-386.0.dev
Merge commit '0f04dc651d5f68ab894e3da13c47e24b2a0d7cb4' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 5644191..d2df8f3 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2021-08-04T21:18:41.561058",
+ "generated": "2021-08-04T16:42:24.433381",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@@ -226,7 +226,7 @@
"name": "dart2native",
"rootUri": "../pkg/dart2native",
"packageUri": "lib/",
- "languageVersion": "2.7"
+ "languageVersion": "2.12"
},
{
"name": "dart_internal",
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
index 655d3dd..d635651 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
@@ -10,7 +10,6 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/resolver/scope.dart';
/// A contributor that produces suggestions based on the members of an
/// extension.
@@ -113,8 +112,7 @@
void _addExtensionMembers(LibraryElement containingLibrary,
CompletionSuggestionKind? kind, DartType type) {
var typeSystem = containingLibrary.typeSystem;
- var nameScope = containingLibrary.scope;
- for (var extension in nameScope.extensions) {
+ for (var extension in containingLibrary.accessibleExtensions) {
var extendedType =
extension.resolvedExtendedType(containingLibrary, type);
if (extendedType != null && typeSystem.isSubtypeOf(type, extendedType)) {
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
index aceacd3..aacc03d 100644
--- a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
+++ b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/ignore_comments/ignore_info.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError, Element;
@@ -234,7 +235,9 @@
/// Leading comments for the first directive in a file are considered library
/// comments and not returned unless they contain blank lines, in which case
/// only the last part of the comment will be returned (unless it is a
- /// language directive comment, in which case it will also be skipped).
+ /// language directive comment, in which case it will also be skipped) or an
+ /// '// ignore:' comment which should always be treated as attached to the
+ /// import.
static Token? getLeadingComment(
CompilationUnit unit, UriBasedDirective directive, LineInfo lineInfo) {
if (directive.beginToken.precedingComments == null) {
@@ -260,22 +263,25 @@
firstComment = firstComment.next;
}
- // Check if the comment is the first comment in the document
- if (firstComment != unit.beginToken.precedingComments) {
- var previousDirectiveLine =
- lineInfo.getLocation(directive.beginToken.previous!.end).lineNumber;
-
- // Skip over any comments on the same line as the previous directive
- // as they will be attached to the end of it.
- var comment = firstComment;
- while (comment != null &&
- previousDirectiveLine ==
- lineInfo.getLocation(comment.offset).lineNumber) {
- comment = comment.next;
- }
- return comment;
+ // If the comment is the first comment in the document then whether we
+ // consider it the leading comment depends on whether it's an ignore comment
+ // or not.
+ if (firstComment != null &&
+ firstComment == unit.beginToken.precedingComments) {
+ return _isIgnoreComment(firstComment) ? firstComment : null;
}
- return null;
+
+ // Skip over any comments on the same line as the previous directive
+ // as they will be attached to the end of it.
+ var previousDirectiveLine =
+ lineInfo.getLocation(directive.beginToken.previous!.end).lineNumber;
+ comment = firstComment;
+ while (comment != null &&
+ previousDirectiveLine ==
+ lineInfo.getLocation(comment.offset).lineNumber) {
+ comment = comment.next;
+ }
+ return comment;
}
/// Gets the last comment token considered to be the trailing comment for this
@@ -295,6 +301,11 @@
}
return null;
}
+
+ /// Returns whether this token is a '// ignore:' comment (but not an
+ /// '// ignore_for_file:' comment).
+ static bool _isIgnoreComment(Token token) =>
+ IgnoreInfo.IGNORE_MATCHER.matchAsPrefix(token.lexeme) != null;
}
class _DirectiveInfo implements Comparable<_DirectiveInfo> {
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index b99492a..f28613f 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -42,6 +42,46 @@
''');
}
+ Future<void> test_ignore_asFirstComment() async {
+ // Usually the first comment is treated as a library comment and not moved
+ // but if it's an 'ignore:' it should be treated as attached to the import.
+ await _computeUnitAndErrors(r'''
+// ignore: unused_import
+import 'dart:io';
+import 'dart:async';
+
+Future a;
+''');
+ // validate change
+ _assertOrganize(r'''
+import 'dart:async';
+// ignore: unused_import
+import 'dart:io';
+
+Future a;
+''');
+ }
+
+ Future<void> test_ignoreForFile_asFirstComment() async {
+ // Unlike 'ignore:', 'ignore_for_file:' still _should_ be kept at the top
+ // of the file and not attached to the import.
+ await _computeUnitAndErrors(r'''
+// ignore_for_file: unused_import
+import 'dart:io';
+import 'dart:async';
+
+Future a;
+''');
+ // validate change
+ _assertOrganize(r'''
+// ignore_for_file: unused_import
+import 'dart:async';
+import 'dart:io';
+
+Future a;
+''');
+ }
+
Future<void> test_keep_duplicateImports_withDifferentPrefix() async {
await _computeUnitAndErrors(r'''
import 'dart:async' as async1;
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 77c23d9..f34732b 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -1300,6 +1300,10 @@
///
/// Clients may not extend, implement or mix-in this class.
abstract class LibraryElement implements _ExistingElement {
+ /// Returns a list containing all of the extension elements accessible within
+ /// this library.
+ List<ExtensionElement> get accessibleExtensions;
+
/// Return the compilation unit that defines this library.
CompilationUnitElement get definingCompilationUnit;
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index ed78b2e..53fdfae 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,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 = 167;
+ static const int DATA_VERSION = 168;
/// 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/unlinked_api_signature.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_api_signature.dart
index 74e302c..6dc30c4 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unlinked_api_signature.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_api_signature.dart
@@ -63,9 +63,7 @@
if (member is ConstructorDeclaration) {
signature.addInt(_kindConstructorDeclaration);
_addTokens(member.beginToken, member.parameters.endToken);
- if (member.constKeyword != null) {
- _addNodeList(member.initializers);
- }
+ _addNodeList(member.initializers);
_addNode(member.redirectedConstructor);
} else if (member is FieldDeclaration) {
signature.addInt(_kindFieldDeclaration);
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index c998b6a..531e95f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -3689,6 +3689,9 @@
super(name, offset);
@override
+ List<ExtensionElement> get accessibleExtensions => scope.extensions;
+
+ @override
CompilationUnitElement get definingCompilationUnit =>
_definingCompilationUnit;
@@ -3942,7 +3945,7 @@
}
@override
- Scope get scope {
+ LibraryScope get scope {
return _scope ??= LibraryScope(this);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index 6ad246d..3f5ac4f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -6,7 +6,6 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/error/listener.dart';
@@ -19,7 +18,6 @@
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/resolution_result.dart';
-import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/util/either.dart';
@@ -37,8 +35,6 @@
bool get _genericMetadataIsEnabled =>
_resolver.definingLibrary.featureSet.isEnabled(Feature.generic_metadata);
- Scope get _nameScope => _resolver.nameScope;
-
TypeProvider get _typeProvider => _resolver.typeProvider;
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
@@ -397,7 +393,7 @@
}
}
- for (var extension in _nameScope.extensions) {
+ for (var extension in _resolver.definingLibrary.accessibleExtensions) {
checkExtension(extension);
}
return candidates;
diff --git a/pkg/analyzer/lib/src/dart/resolver/scope.dart b/pkg/analyzer/lib/src/dart/resolver/scope.dart
index 845c8e1..83c03c4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/scope.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/scope.dart
@@ -6,9 +6,7 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/src/dart/element/element.dart';
-import 'package:analyzer/src/dart/element/scope.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.dart';
@@ -350,20 +348,3 @@
return null;
}
}
-
-extension ScopeExtension on Scope {
- List<ExtensionElement> get extensions {
- return _enclosingLibraryScope.extensions;
- }
-
- LibraryScope get _enclosingLibraryScope {
- Scope? scope = this;
- while (scope != null) {
- if (scope is LibraryScope) {
- return scope;
- }
- scope = (scope as EnclosedScope).parent;
- }
- throw StateError('Can only be used in a LibraryScope.');
- }
-}
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 6a09580..4c75b2a 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -2507,6 +2507,7 @@
nameScope,
element.typeParameters,
);
+ _setNodeNameScope(node, nameScope);
visitClassDeclarationInScope(node);
nameScope = ClassScope(nameScope, element);
@@ -2669,6 +2670,7 @@
nameScope,
element.typeParameters,
);
+ _setNodeNameScope(node, nameScope);
visitExtensionDeclarationInScope(node);
nameScope = ExtensionScope(nameScope, element);
@@ -2788,6 +2790,7 @@
nameScope,
element.typeParameters,
);
+ _setNodeNameScope(node, nameScope);
visitFunctionDeclarationInScope(node);
} finally {
nameScope = outerScope;
@@ -2895,6 +2898,7 @@
GenericFunctionTypeElement element =
(node as GenericFunctionTypeImpl).declaredElement!;
nameScope = TypeParameterScope(nameScope, element.typeParameters);
+ _setNodeNameScope(node, nameScope);
super.visitGenericFunctionType(node);
} finally {
nameScope = outerScope;
@@ -2908,6 +2912,7 @@
try {
var element = node.declaredElement as TypeAliasElement;
nameScope = TypeParameterScope(nameScope, element.typeParameters);
+ _setNodeNameScope(node, nameScope);
visitGenericTypeAliasInScope(node);
var aliasedElement = element.aliasedElement;
@@ -2962,6 +2967,7 @@
nameScope,
element.typeParameters,
);
+ _setNodeNameScope(node, nameScope);
visitMethodDeclarationInScope(node);
} finally {
nameScope = outerScope;
@@ -2990,6 +2996,7 @@
node.metadata.accept(this);
nameScope = TypeParameterScope(nameScope, element.typeParameters);
+ _setNodeNameScope(node, nameScope);
visitMixinDeclarationInScope(node);
nameScope = ClassScope(nameScope, element);
diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
index da1447b..0c63950 100644
--- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
@@ -66,14 +66,11 @@
}
void resolveConstructorNode(ConstructorDeclarationImpl node) {
- var isConst = node.constKeyword != null;
// We don't want to visit the whole node because that will try to create an
// element for it; we just want to process its children so that we can
// resolve initializers and/or a redirection.
void visit(AstVisitor<Object?> visitor) {
- if (isConst) {
- node.initializers.accept(visitor);
- }
+ node.initializers.accept(visitor);
node.redirectedConstructor?.accept(visitor);
}
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index 51a33b6..2c9d02c 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -162,11 +162,8 @@
);
reader._addFormalParameters(element.parameters);
_readFormalParameters(reader, element.parameters);
- if (element.isConst || element.isFactory) {
- element.redirectedConstructor =
- reader.readElement() as ConstructorElement?;
- element.constantInitializers = reader._readNodeList();
- }
+ element.redirectedConstructor = reader.readElement() as ConstructorElement?;
+ element.constantInitializers = reader._readNodeList();
applyConstantOffsets?.perform();
}
}
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 5fd0bf8..4b67169 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -165,10 +165,8 @@
try {
_writeList(element.parameters, _writeParameterElement);
_writeMacro(element.macro);
- if (element.isConst || element.isFactory) {
- _resolutionSink.writeElement(element.redirectedConstructor);
- _resolutionSink._writeNodeList(element.constantInitializers);
- }
+ _resolutionSink.writeElement(element.redirectedConstructor);
+ _resolutionSink._writeNodeList(element.constantInitializers);
} finally {
_resolutionSink.localElements.popScope();
}
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index bc471b2..195647b 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -285,6 +285,10 @@
findInClass(class_);
}
+ for (var extension_ in unitElement.extensions) {
+ findIn(extension_.typeParameters);
+ }
+
for (var mixin in unitElement.mixins) {
findInClass(mixin);
}
diff --git a/pkg/analyzer/test/src/dart/analysis/unlinked_api_signature_test.dart b/pkg/analyzer/test/src/dart/analysis/unlinked_api_signature_test.dart
index 1507f47..5d2201c 100644
--- a/pkg/analyzer/test/src/dart/analysis/unlinked_api_signature_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/unlinked_api_signature_test.dart
@@ -117,7 +117,7 @@
}
test_class_constructor_initializer_empty() {
- assertSameSignature(r'''
+ assertNotSameSignature(r'''
class C {
C.foo() : ;
}
@@ -128,8 +128,9 @@
''');
}
+ /// See https://github.com/dart-lang/sdk/issues/46206
test_class_constructor_initializer_notConst() {
- assertSameSignature(r'''
+ assertNotSameSignature(r'''
class C {
final int f;
C.foo() : f = 1;
diff --git a/pkg/analyzer/test/src/lint/linter/resolve_name_in_scope_test.dart b/pkg/analyzer/test/src/lint/linter/resolve_name_in_scope_test.dart
index 036ee36..0520a63 100644
--- a/pkg/analyzer/test/src/lint/linter/resolve_name_in_scope_test.dart
+++ b/pkg/analyzer/test/src/lint/linter/resolve_name_in_scope_test.dart
@@ -68,6 +68,38 @@
_checkGetterDifferent(import.topSet('foo'));
}
+ test_class_getter_fromExtends_blockBody() async {
+ await resolve('''
+class A {
+ int get foo => 0;
+}
+
+class B extends A {
+ void bar(int foo) {
+ this.foo;
+ }
+}
+''');
+ _checkGetterRequested(
+ findElement.parameter('foo'),
+ );
+ }
+
+ test_class_getter_fromExtends_expressionBody() async {
+ await resolve('''
+class A {
+ int get foo => 0;
+}
+
+class B extends A {
+ void bar(int foo) => this.foo;
+}
+''');
+ _checkGetterRequested(
+ findElement.parameter('foo'),
+ );
+ }
+
test_class_getter_none_fromExtends() async {
await resolve('''
class A {
@@ -408,6 +440,16 @@
_checkMethodRequested(findElement.typeParameter('foo'));
}
+ test_class_method_typeParameter() async {
+ await resolve('''
+class A {
+ void foo<T>(int T) {}
+}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
test_class_setter_different_formalParameter_constructor() async {
await resolve('''
class A {
@@ -538,6 +580,46 @@
_checkSetterRequested(findElement.setter('foo'));
}
+ test_class_typeParameter_inConstructor() async {
+ await resolve('''
+class A<T> {
+ A(int T) {}
+}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
+ test_class_typeParameter_inField() async {
+ await resolve('''
+class A<T> {
+ T? a;
+}
+''');
+ var node = findNode.simple('T?');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
+ test_class_typeParameter_inMethod() async {
+ await resolve('''
+class A<T> {
+ void foo(int T) {}
+}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
+ test_class_typeParameter_inSetter() async {
+ await resolve('''
+class A<T> {
+ set foo(int T) {}
+}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
test_extension_method_none_fromExtended() async {
await resolve('''
class A {
@@ -608,6 +690,41 @@
_checkMethodRequested(findElement.method('foo'));
}
+ test_extension_typeParameter_inMethod() async {
+ await resolve('''
+extension E<T> on int {
+ void foo(int T) {}
+}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
+ test_function_typeParameter() async {
+ await resolve('''
+void foo<T>(int T) {}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
+ test_genericFunctionType_typeParameter() async {
+ await resolve('''
+void foo(void Function<T>(String T) b) {}
+''');
+ var node = findNode.simple('T)');
+ var T = findNode.typeParameter('T>').declaredElement!;
+ _resultRequested(node, 'T', false, T);
+ }
+
+ test_genericTypeAlias_typeParameter() async {
+ await resolve('''
+typedef A<T> = List<T>;
+''');
+ var node = findNode.simple('T>;');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
test_mixin_method_requested_formalParameter_method() async {
await resolve('''
mixin M {
@@ -634,6 +751,26 @@
_checkMethodRequested(findElement.method('foo'));
}
+ test_mixin_typeParameter_inField() async {
+ await resolve('''
+mixin A<T> {
+ T? a;
+}
+''');
+ var node = findNode.simple('T?');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
+ test_mixin_typeParameter_inMethod() async {
+ await resolve('''
+mixin A<T> {
+ void foo(int T) {}
+}
+''');
+ var node = findNode.simple('T)');
+ _resultRequested(node, 'T', false, findElement.typeParameter('T'));
+ }
+
void _checkGetterDifferent(Element expected) {
var node = findNode.this_('this.foo;');
_resultDifferent(node, 'foo', false, expected);
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 8473b16..9adf69a 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -13388,6 +13388,7 @@
periodOffset: 13
nameEnd: 19
@25
+ redirectedConstructor: self::@class::C::@constructor::named
''');
}
@@ -13467,6 +13468,7 @@
named @21
periodOffset: 20
nameEnd: 26
+ redirectedConstructor: self::@class::C::@constructor::•
''');
}
diff --git a/pkg/compiler/pubspec.yaml b/pkg/compiler/pubspec.yaml
index 1073c93..ada6b1e 100644
--- a/pkg/compiler/pubspec.yaml
+++ b/pkg/compiler/pubspec.yaml
@@ -39,6 +39,7 @@
path: any
source_maps: any
test: any
+ cli_util: any
# Unpublished packages that can be used via path dependency
async_helper:
path: ../async_helper
diff --git a/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.dart b/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.dart
new file mode 100644
index 0000000..13cc373
--- /dev/null
+++ b/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.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 "dart:io";
+import "package:kernel/kernel.dart";
+import "package:kernel/ast.dart";
+
+main(List<String> args) async {
+ // Ensure right args are passed.
+ if (args.length < 1) {
+ print("usage: ${Platform.script} a.dill");
+ }
+
+ // Parse .dill and extract component.
+ var dill = args[0];
+ var component = loadComponentFromBinary(dill);
+ var visitor = MetricsVisitor(["dart:html"]);
+
+ // Visit component.
+ component.accept(visitor);
+
+ // Save data to file.
+ visitor.saveDataToFile("dart2html_metrics.json");
+}
+
+/// Visits classes in libraries specified by `libraryFilter`
+/// and aggregates metrics by class.
+class MetricsVisitor extends RecursiveVisitor {
+ String currentClass;
+ List<String> libraryFilter;
+ Map<String, ClassMetrics> classInfo = {};
+
+ MetricsVisitor([filter]) {
+ libraryFilter = filter ?? [];
+ }
+
+ @override
+ void visitComponent(Component node) {
+ super.visitComponent(node);
+ _processData();
+ }
+
+ @override
+ void visitLibrary(Library node) {
+ // Check if this is a library we want to visit.
+ var visit = libraryFilter.isNotEmpty
+ ? libraryFilter
+ .contains("${node.importUri.scheme}:${node.importUri.path}")
+ : true;
+
+ if (visit) {
+ super.visitLibrary(node);
+ }
+ }
+
+ @override
+ void visitProcedure(Procedure node) {
+ classInfo[currentClass].methods.add(ClassMetricsMethod(
+ node.name.text,
+ node.containsSuperCalls,
+ node.isInstanceMember,
+ node.isExternal,
+ node.isAbstract,
+ node.kind.toString()));
+ }
+
+ @override
+ void visitClass(Class node) {
+ // Dont want to add duplicate info.
+ // When mixed, anonymous mixin class generated so we want to ignore.
+ if (!node.isAnonymousMixin) {
+ currentClass = node.name;
+ var metrics = ClassMetrics();
+
+ // Check if class contains native members.
+ if (node.annotations.any(_isNativeMarkerAnnotation)) {
+ metrics.containsNativeMember = true;
+ }
+
+ // Check if Mixed.
+ if (node.superclass?.isAnonymousMixin ?? false) {
+ metrics.mixed = true;
+ metrics.mixins = _filterMixins(node.superclass.demangledName);
+ }
+
+ // Add parents.
+ if (node.superclass != null) {
+ var unmangledParent = _getParent(node.superclass);
+ metrics.parent = unmangledParent;
+ }
+
+ // Check for implemented classes.
+ if (node.implementedTypes.length > 0) {
+ var implementedTypes =
+ node.implementedTypes.map((type) => type.className.asClass.name);
+ metrics.implementedTypes = implementedTypes.toList();
+ }
+
+ classInfo[currentClass] = metrics;
+
+ super.visitClass(node);
+ }
+ }
+
+ // Returns List of parsed mixins from superclass name.
+ List<String> _filterMixins(String superWithMixins) {
+ var start = superWithMixins.indexOf("with") + 4;
+ var mixins = superWithMixins.substring(start);
+ mixins = mixins.replaceAll(" ", "");
+
+ return mixins.split(",");
+ }
+
+ // Recursively searches superclasses, filtering anonymous mixins,
+ // and returns parent class name.
+ String _getParent(Class node) {
+ if (node.isAnonymousMixin) {
+ return _getParent(node.superclass);
+ }
+
+ return node.name;
+ }
+
+ // Returns true if a class Annotation is Native.
+ bool _isNativeMarkerAnnotation(Expression annotation) {
+ if (annotation is ConstructorInvocation) {
+ var type = annotation.constructedType;
+ if (type.classNode.name == "Native") {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Passes through the aggregated data and processes,
+ // adding child classes and overridden methods from parent.
+ void _processData() {
+ classInfo.keys.forEach((className) {
+ var parentName = classInfo[className].parent;
+ if (classInfo[parentName] != null) {
+ classInfo[parentName].inheritedBy.add(className);
+
+ var notOverridden = <String>[];
+ var parentMethods = classInfo[parentName].methods.map((m) => m.name);
+ var classMethods = classInfo[className].methods.map((m) => m.name);
+
+ parentMethods.forEach((method) =>
+ {if (!classMethods.contains(method)) notOverridden.add(method)});
+
+ // Update Method Info.
+ classInfo[className].notOverriddenMethods = notOverridden;
+ }
+ });
+ }
+
+ // Saves the data to file.
+ void saveDataToFile(String filename) {
+ var formatted = jsonFormat(classInfo);
+
+ File(filename).writeAsStringSync(formatted);
+ }
+
+ // Converts the passed Map to a pretty print JSON string.
+ String jsonFormat(Map<String, ClassMetrics> info) {
+ JsonEncoder encoder = new JsonEncoder.withIndent(" ");
+ return encoder.convert(info);
+ }
+}
+
+/// Tracks info compiled for a class.
+class ClassMetrics {
+ List<ClassMetricsMethod> methods;
+ List<String> mixins;
+ List<String> implementedTypes;
+ List<String> notOverriddenMethods;
+ List<String> inheritedBy;
+ String parent;
+ bool mixed;
+ bool containsNativeMember;
+
+ ClassMetrics(
+ {this.mixed = false,
+ this.containsNativeMember = false,
+ this.parent,
+ methods,
+ mixins,
+ notOverridden,
+ implementedTypes,
+ inheritedBy}) {
+ this.methods = methods ?? [];
+ this.mixins = mixins ?? [];
+ this.notOverriddenMethods = notOverridden ?? [];
+ this.implementedTypes = implementedTypes ?? [];
+ this.inheritedBy = inheritedBy ?? [];
+ }
+
+ bool get invokesSuper {
+ if (methods.isNotEmpty) {
+ return methods.any((e) => e.invokesSuper);
+ }
+
+ return false;
+ }
+
+ Map<String, dynamic> toJson() {
+ return {
+ "invokesSuper": invokesSuper,
+ "methods": methods,
+ "mixed": mixed,
+ "mixins": mixins,
+ "parent": parent,
+ "inheritedBy": inheritedBy,
+ "containsNativeMember": containsNativeMember,
+ "notOverriddenMethods": notOverriddenMethods,
+ "implementedTypes": implementedTypes
+ };
+ }
+}
+
+/// Tracks info related to a specific method.
+class ClassMetricsMethod {
+ String name;
+ String methodKind;
+ bool invokesSuper;
+ bool isInstanceMember;
+ bool isExternal;
+ bool isAbstract;
+
+ ClassMetricsMethod(this.name,
+ [this.invokesSuper = false,
+ this.isInstanceMember = false,
+ this.isExternal = false,
+ this.isAbstract = false,
+ this.methodKind = ""]);
+
+ Map<String, dynamic> toJson() {
+ return {
+ "name": name,
+ "invokesSuper": invokesSuper,
+ "isInstanceMember": isInstanceMember,
+ "isExternal": isExternal,
+ "isAbstract": isAbstract,
+ "methodKind": methodKind
+ };
+ }
+}
diff --git a/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart b/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart
new file mode 100644
index 0000000..1e24648
--- /dev/null
+++ b/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart
@@ -0,0 +1,96 @@
+// 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 "package:kernel/kernel.dart";
+import "package:expect/expect.dart";
+import "package:expect/minitest.dart";
+import "../dart_html_metrics_visitor.dart";
+import "package:cli_util/cli_util.dart";
+import "package:path/path.dart" as path;
+import "dart:io";
+
+main() async {
+ // Compile Dill
+ var sdkPath = getSdkPath();
+ if (!sdkPath.contains("ReleaseX64")) sdkPath = path.join(sdkPath, "ReleaseX64", "dart-sdk");
+ var scriptPath = Platform.script.path;
+ var pkgPath = path.dirname(
+ path.dirname(path.dirname(path.dirname(path.dirname(scriptPath)))));
+ var compilePath = path.canonicalize(
+ path.join(pkgPath, "front_end", "tool", "_fasta", "compile.dart"));
+ var testClassesPath = path
+ .canonicalize(path.join(path.dirname(scriptPath), "test_classes.dart"));
+ var ddcOutlinePath =
+ path.canonicalize(path.join(sdkPath, "lib", "_internal", "ddc_sdk.dill"));
+ var dillPath = path
+ .canonicalize(path.join(path.dirname(scriptPath), "test_classes.dill"));
+
+ await Process.run(path.join(sdkPath, "bin", "dart"), [
+ compilePath,
+ "--target=dartdevc",
+ "--platform=${ddcOutlinePath}",
+ "-o=${dillPath}",
+ testClassesPath
+ ]);
+
+ // Dill compiled from test_classes.dart using ddc.
+ var component = loadComponentFromBinary(dillPath);
+ var visitor = MetricsVisitor(["file:${testClassesPath}"]);
+
+ component.accept(visitor);
+
+ test("Class A does not call super", () {
+ Expect.equals(visitor.classInfo["A"].invokesSuper, false);
+ });
+
+ test("Class B does call super", () {
+ Expect.equals(visitor.classInfo["B"].invokesSuper, true);
+
+ var callingMethod = visitor.classInfo["B"].methods
+ .where((m) => m.name == "testSuper")
+ .toList()[0];
+ Expect.equals(callingMethod.invokesSuper, true);
+ });
+
+ test("Class C does not call super", () {
+ Expect.equals(visitor.classInfo["C"].invokesSuper, false);
+ });
+
+ test("Class A inherited by B", () {
+ Expect.equals(visitor.classInfo["A"].inheritedBy.contains("B"), true);
+ Expect.equals(visitor.classInfo["B"].parent, "A");
+ });
+
+ test("Class B inherited by C", () {
+ Expect.equals(visitor.classInfo["B"].inheritedBy.contains("C"), true);
+ Expect.equals(visitor.classInfo["C"].parent, "B");
+ });
+
+ test("Class B inherited by F", () {
+ Expect.equals(visitor.classInfo["B"].inheritedBy.contains("F"), true);
+ Expect.equals(visitor.classInfo["F"].parent, "B");
+ });
+
+ test("Class C inherited by nothing", () {
+ Expect.equals(visitor.classInfo["C"].inheritedBy.length, 0);
+ });
+
+ test("Class D mixed with Mix1 and Mix2", () {
+ Expect.equals(visitor.classInfo["D"].mixed, true);
+ Expect.deepEquals(visitor.classInfo["D"].mixins, ["Mix1", "Mix2"]);
+ });
+
+ test("Class F mixed with Mix1 and Mix2", () {
+ Expect.equals(visitor.classInfo["F"].mixed, true);
+ Expect.deepEquals(visitor.classInfo["F"].mixins, ["Mix1", "Mix2"]);
+ });
+
+ test("Class E implements A", () {
+ Expect.equals(visitor.classInfo["E"].implementedTypes.contains("A"), true);
+ });
+
+ test("Class G extends A but fails to override getValue()", () {
+ Expect.equals(
+ visitor.classInfo["G"].notOverriddenMethods.contains("getValue"), true);
+ });
+}
diff --git a/pkg/compiler/tool/kernel_visitor/test/test_classes.dart b/pkg/compiler/tool/kernel_visitor/test/test_classes.dart
new file mode 100644
index 0000000..2b02ef8
--- /dev/null
+++ b/pkg/compiler/tool/kernel_visitor/test/test_classes.dart
@@ -0,0 +1,60 @@
+// 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.
+library info_visitor_test_classes;
+
+// Test superclass value.
+class A {
+ A();
+
+ getValue() {
+ return "Value";
+ }
+}
+
+// Test subclass calling "super".
+class B extends A {
+ B();
+
+ testSuper() {
+ return super.getValue();
+ }
+}
+
+// Test subclass not calling "super".
+class C extends B {
+ C();
+
+ @override
+ getValue() {
+ return "Value";
+ }
+}
+
+// Test class with mixins.
+class Mix1 {}
+
+class Mix2 {}
+
+class D with Mix1, Mix2 {
+ D();
+}
+
+class F extends B with Mix1, Mix2 {
+ F();
+}
+
+// Test class with interface
+class E implements A {
+ E();
+
+ @override
+ getValue() {
+ return "E Value";
+ }
+}
+
+// Test class with unoverriden superclass method
+class G extends A {
+ G();
+}
diff --git a/pkg/dart2native/bin/dart2native.dart b/pkg/dart2native/bin/dart2native.dart
index d5d888a..f8e95ef 100644
--- a/pkg/dart2native/bin/dart2native.dart
+++ b/pkg/dart2native/bin/dart2native.dart
@@ -21,7 +21,7 @@
Future<void> main(List<String> args) async {
// If we're outputting to a terminal, wrap usage text to that width.
- int outputLineWidth;
+ int? outputLineWidth;
try {
outputLineWidth = stdout.terminalColumns;
} catch (_) {/* Ignore. */}
diff --git a/pkg/dart2native/lib/dart2native.dart b/pkg/dart2native/lib/dart2native.dart
index 3329a8d..f5f0dfe 100644
--- a/pkg/dart2native/lib/dart2native.dart
+++ b/pkg/dart2native/lib/dart2native.dart
@@ -44,7 +44,7 @@
String platformDill,
String sourceFile,
String kernelFile,
- String packages,
+ String? packages,
List<String> defines,
{String enableExperiment = '',
List<String> extraGenKernelOptions = const []}) {
@@ -68,7 +68,7 @@
String genSnapshot,
String kernelFile,
String snapshotFile,
- String debugFile,
+ String? debugFile,
bool enableAsserts,
List<String> extraGenSnapshotOptions) {
return Process.run(genSnapshot, [
diff --git a/pkg/dart2native/lib/generate.dart b/pkg/dart2native/lib/generate.dart
index 8c773d6..23797c9 100644
--- a/pkg/dart2native/lib/generate.dart
+++ b/pkg/dart2native/lib/generate.dart
@@ -21,14 +21,14 @@
Future<void> generateNative({
String kind = 'exe',
- String sourceFile,
- String outputFile,
- String debugFile,
- String packages,
- List<String> defines,
+ required String sourceFile,
+ String? outputFile,
+ String? debugFile,
+ String? packages,
+ required List<String> defines,
String enableExperiment = '',
bool enableAsserts = false,
- bool soundNullSafety,
+ bool? soundNullSafety,
bool verbose = false,
String verbosity = 'all',
List<String> extraOptions = const [],
@@ -42,13 +42,13 @@
final Kind outputKind = {
'aot': Kind.aot,
'exe': Kind.exe,
- }[kind];
+ }[kind]!;
final sourceWithoutDart = sourcePath.replaceFirst(RegExp(r'\.dart$'), '');
final outputPath = path.canonicalize(path.normalize(outputFile ??
{
Kind.aot: '$sourceWithoutDart.aot',
Kind.exe: '$sourceWithoutDart.exe',
- }[outputKind]));
+ }[outputKind]!));
final debugPath =
debugFile != null ? path.canonicalize(path.normalize(debugFile)) : null;
diff --git a/pkg/dart2native/pubspec.yaml b/pkg/dart2native/pubspec.yaml
index 1e858a5..439a0cb 100644
--- a/pkg/dart2native/pubspec.yaml
+++ b/pkg/dart2native/pubspec.yaml
@@ -3,7 +3,7 @@
publish_to: none
environment:
- sdk: "^2.7.0"
+ sdk: '>=2.12.0 <3.0.0'
# Add the bin/dart2native.dart script to the scripts pub installs.
executables:
diff --git a/pkg/front_end/lib/src/api_unstable/dart2js.dart b/pkg/front_end/lib/src/api_unstable/dart2js.dart
index e811ba1..b8d1d74 100644
--- a/pkg/front_end/lib/src/api_unstable/dart2js.dart
+++ b/pkg/front_end/lib/src/api_unstable/dart2js.dart
@@ -241,3 +241,23 @@
// TODO(sigmund): Delete this API once `member.isRedirectingFactory`
// is implemented correctly for patch files (Issue #33495).
bool isRedirectingFactory(ir.Procedure member) => member.isRedirectingFactory;
+
+/// Determines what `ProcedureKind` the given extension [member] is based on
+/// the member name.
+///
+/// Note: classifies operators as `ProcedureKind.Method`.
+ir.ProcedureKind getExtensionMemberKind(ir.Procedure member) {
+ assert(member.isExtensionMember);
+ String name = member.name.text;
+ int pipeIndex = name.indexOf('|');
+ int poundIndex = name.indexOf('#');
+ assert(pipeIndex >= 0);
+ if (poundIndex >= 0) {
+ String getOrSet = name.substring(pipeIndex + 1, poundIndex);
+ return getOrSet == 'get'
+ ? ir.ProcedureKind.Getter
+ : ir.ProcedureKind.Setter;
+ } else {
+ return member.kind;
+ }
+}
diff --git a/pkg/front_end/test/desugar_test.dart b/pkg/front_end/test/desugar_test.dart
index f8078d1..b74e0bd 100644
--- a/pkg/front_end/test/desugar_test.dart
+++ b/pkg/front_end/test/desugar_test.dart
@@ -28,6 +28,7 @@
await testRedirectingFactoryDirect();
await testRedirectingFactorySerialized();
await testRedirectingFactoryPatchFile();
+ await testExtensionMemberKind();
});
}
@@ -79,3 +80,97 @@
_B(int x);
}
''';
+
+testExtensionMemberKind() async {
+ var component = await compileUnit(['e.dart'], {'e.dart': extensionSource});
+ var library = component.libraries
+ .firstWhere((l) => l.importUri.path.endsWith('e.dart'));
+ var descriptors =
+ library.extensions.expand((extension) => extension.members).toList();
+
+ // Check generated getters and setters for fields.
+ var fieldGetter =
+ findExtensionField(descriptors, 'field', ir.ExtensionMemberKind.Getter);
+ Expect.equals(
+ api.getExtensionMemberKind(fieldGetter), ir.ProcedureKind.Getter);
+ var fieldSetter =
+ findExtensionField(descriptors, 'field', ir.ExtensionMemberKind.Setter);
+ Expect.equals(
+ api.getExtensionMemberKind(fieldSetter), ir.ProcedureKind.Setter);
+ var staticFieldGetter = findExtensionField(
+ descriptors, 'staticField', ir.ExtensionMemberKind.Getter);
+ Expect.equals(
+ api.getExtensionMemberKind(staticFieldGetter), ir.ProcedureKind.Getter);
+ var staticFieldSetter = findExtensionField(
+ descriptors, 'staticField', ir.ExtensionMemberKind.Setter);
+ Expect.equals(
+ api.getExtensionMemberKind(staticFieldSetter), ir.ProcedureKind.Setter);
+
+ // Check getters and setters.
+ var getter = findExtensionMember(descriptors, 'getter');
+ Expect.equals(api.getExtensionMemberKind(getter), ir.ProcedureKind.Getter);
+ var setter = findExtensionMember(descriptors, 'setter');
+ Expect.equals(api.getExtensionMemberKind(setter), ir.ProcedureKind.Setter);
+ var staticGetter = findExtensionMember(descriptors, 'staticGetter');
+ Expect.equals(
+ api.getExtensionMemberKind(staticGetter), ir.ProcedureKind.Getter);
+ var staticSetter = findExtensionMember(descriptors, 'staticSetter');
+ Expect.equals(
+ api.getExtensionMemberKind(staticSetter), ir.ProcedureKind.Setter);
+
+ // Check methods.
+ var method = findExtensionMember(descriptors, 'method');
+ Expect.equals(api.getExtensionMemberKind(method), ir.ProcedureKind.Method);
+ var methodTearoff = findExtensionTearoff(descriptors, 'get#method');
+ Expect.equals(
+ api.getExtensionMemberKind(methodTearoff), ir.ProcedureKind.Getter);
+ var staticMethod = findExtensionMember(descriptors, 'staticMethod');
+ Expect.equals(
+ api.getExtensionMemberKind(staticMethod), ir.ProcedureKind.Method);
+
+ // Check operators.
+ var operator = findExtensionMember(descriptors, '+');
+ Expect.equals(api.getExtensionMemberKind(operator), ir.ProcedureKind.Method);
+}
+
+ir.Member findExtensionMember(
+ List<ir.ExtensionMemberDescriptor> descriptors, String memberName) {
+ return descriptors
+ .firstWhere((d) => d.name.text == memberName)
+ .member
+ .asMember;
+}
+
+ir.Member findExtensionField(List<ir.ExtensionMemberDescriptor> descriptors,
+ String fieldName, ir.ExtensionMemberKind kind) {
+ return descriptors
+ .firstWhere((d) => d.name.text == fieldName && d.kind == kind)
+ .member
+ .asMember;
+}
+
+ir.Member findExtensionTearoff(
+ List<ir.ExtensionMemberDescriptor> descriptors, String memberName) {
+ return descriptors
+ .map((d) => d.member.asMember)
+ .firstWhere((m) => m.name.text.contains(memberName));
+}
+
+const extensionSource = '''
+class Foo {}
+
+extension Ext on Foo {
+ external int field;
+ external static int staticField;
+
+ external get getter;
+ external set setter(_);
+ external static get staticGetter;
+ external static set staticSetter(_);
+
+ external int method();
+ external static int staticMethod();
+
+ external operator +(_);
+}
+''';
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index aed39dd..201c0e11 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -391,8 +391,8 @@
enforced
enforces
enforcing
-enters
engineered
+enters
enumerates
env
eof
@@ -896,6 +896,7 @@
physically
pi
picking
+pipe
pkg
play
player
@@ -911,6 +912,7 @@
pos
possibility
postfix
+pound
pow
pragma
pre
diff --git a/runtime/tests/vm/dart/regress_46799_test.dart b/runtime/tests/vm/dart/regress_46799_test.dart
new file mode 100644
index 0000000..0d44c95
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_46799_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// Reduced from
+// The Dart Project Fuzz Tester (1.91).
+// Program generated as:
+// dart dartfuzz.dart --seed 1052527605 --no-fp --no-ffi --flat
+
+bool var31 = bool.hasEnvironment('z');
+
+@pragma('vm:never-inline')
+num foo0() {
+ print(var31);
+ return -4294967296;
+}
+
+main() {
+ print(((-67) ~/ (var31 ? -83 : foo0())));
+}
diff --git a/runtime/tests/vm/dart_2/regress_46799_test.dart b/runtime/tests/vm/dart_2/regress_46799_test.dart
new file mode 100644
index 0000000..0d44c95
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_46799_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// Reduced from
+// The Dart Project Fuzz Tester (1.91).
+// Program generated as:
+// dart dartfuzz.dart --seed 1052527605 --no-fp --no-ffi --flat
+
+bool var31 = bool.hasEnvironment('z');
+
+@pragma('vm:never-inline')
+num foo0() {
+ print(var31);
+ return -4294967296;
+}
+
+main() {
+ print(((-67) ~/ (var31 ? -83 : foo0())));
+}
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 2b984a0..c531f98 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -59,7 +59,7 @@
// StorageTrait for HashTable which allows to create hash tables backed by
// zone memory. Used to compute cluster order for canonical clusters.
struct GrowableArrayStorageTraits {
- class Array {
+ class Array : public ZoneAllocated {
public:
explicit Array(Zone* zone, intptr_t length)
: length_(length), array_(zone->Alloc<ObjectPtr>(length)) {}
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index 06edf78..acb00ca 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -1348,8 +1348,9 @@
// TODO(alexmarkov): calculate type of InstanceCallInstr eagerly
// (in optimized mode) and avoid keeping separate result_type.
CompileType* inferred_type = result_type();
- if ((inferred_type != NULL) &&
- (inferred_type->ToNullableCid() != kDynamicCid)) {
+ if ((inferred_type != nullptr) &&
+ ((inferred_type->ToNullableCid() != kDynamicCid) ||
+ (!inferred_type->ToAbstractType()->IsDynamicType()))) {
TraceStrongModeType(this, inferred_type);
return *inferred_type;
}
@@ -1441,8 +1442,11 @@
if (is_known_list_constructor()) {
return ComputeListFactoryType(inferred_type, ArgumentValueAt(0));
}
- if ((inferred_type != NULL) &&
- (inferred_type->ToNullableCid() != kDynamicCid)) {
+
+ if ((inferred_type != nullptr) &&
+ ((inferred_type->ToNullableCid() != kDynamicCid) ||
+ (!inferred_type->ToAbstractType()->IsDynamicType()))) {
+ TraceStrongModeType(this, inferred_type);
return *inferred_type;
}
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 182c090..999790d 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -2274,7 +2274,10 @@
instance_ ^= value.ptr();
if (!instance_.IsAssignableTo(type_, instantiator_type_arguments_,
function_type_arguments_)) {
- ASSERT(!FLAG_identity_reload);
+ // Even if doing an identity reload, type check can fail if hot reload
+ // happens while constructor is still running and field is not
+ // initialized yet, so it has a null value.
+ ASSERT(!FLAG_identity_reload || instance_.IsNull());
field.set_needs_load_guard(true);
} else {
cache_.AddCheck(instance_cid_or_signature_, type_,
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index 1c5e2db..b9400ee 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -780,13 +780,17 @@
objects_.Add(instance);
const intptr_t next_field_offset = next_field_offset_;
+#if defined(DART_PRECOMPILED_RUNTIME)
const auto unboxed_fields_bitmap =
s->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(cid_);
+#endif
for (intptr_t offset = Instance::NextFieldOffset();
offset < next_field_offset; offset += kCompressedWordSize) {
+#if defined(DART_PRECOMPILED_RUNTIME)
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
continue;
}
+#endif
s->Push(reinterpret_cast<CompressedObjectPtr*>(
reinterpret_cast<uword>(instance->untag()) + offset)
->Decompress(instance->untag()->heap_base()));
@@ -810,20 +814,24 @@
Instance* instance = objects_[i];
const intptr_t next_field_offset = next_field_offset_;
+#if defined(DART_PRECOMPILED_RUNTIME)
const auto unboxed_fields_bitmap =
s->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(cid_);
+#endif
for (intptr_t offset = Instance::NextFieldOffset();
offset < next_field_offset; offset += kCompressedWordSize) {
+#if defined(DART_PRECOMPILED_RUNTIME)
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
// Writes 32 bits of the unboxed value at a time
const uword value = *reinterpret_cast<compressed_uword*>(
reinterpret_cast<uword>(instance->untag()) + offset);
s->WriteWordWith32BitWrites(value);
- } else {
- s->WriteRef(reinterpret_cast<CompressedObjectPtr*>(
- reinterpret_cast<uword>(instance->untag()) + offset)
- ->Decompress(instance->untag()->heap_base()));
+ continue;
}
+#endif
+ s->WriteRef(reinterpret_cast<CompressedObjectPtr*>(
+ reinterpret_cast<uword>(instance->untag()) + offset)
+ ->Decompress(instance->untag()->heap_base()));
}
}
}
@@ -853,37 +861,44 @@
void ReadEdges(MessageDeserializer* d) {
const intptr_t next_field_offset = cls_.host_next_field_offset();
- const intptr_t type_argument_field_offset =
- cls_.host_type_arguments_field_offset();
+#if defined(DART_PRECOMPILED_RUNTIME)
const auto unboxed_fields_bitmap =
d->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
cls_.id());
+#else
+ const intptr_t type_argument_field_offset =
+ cls_.host_type_arguments_field_offset();
const bool use_field_guards = d->isolate_group()->use_field_guards();
const Array& field_map = Array::Handle(d->zone(), cls_.OffsetToFieldMap());
+ Field& field = Field::Handle(d->zone());
+#endif
Instance& instance = Instance::Handle(d->zone());
Object& value = Object::Handle(d->zone());
- Field& field = Field::Handle(d->zone());
for (intptr_t id = start_index_; id < stop_index_; id++) {
instance ^= d->Ref(id);
for (intptr_t offset = Instance::NextFieldOffset();
offset < next_field_offset; offset += kCompressedWordSize) {
+#if defined(DART_PRECOMPILED_RUNTIME)
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
compressed_uword* p = reinterpret_cast<compressed_uword*>(
reinterpret_cast<uword>(instance.untag()) + offset);
// Reads 32 bits of the unboxed value at a time
*p = d->ReadWordWith32BitReads();
- } else {
- value = d->ReadRef();
- instance.SetFieldAtOffset(offset, value);
- if (use_field_guards && (offset != type_argument_field_offset) &&
- (value.ptr() != Object::sentinel().ptr())) {
- field ^= field_map.At(offset >> kCompressedWordSizeLog2);
- ASSERT(!field.IsNull());
- ASSERT(field.HostOffset() == offset);
- field.RecordStore(value);
- }
+ continue;
}
+#endif
+ value = d->ReadRef();
+ instance.SetFieldAtOffset(offset, value);
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ if (use_field_guards && (offset != type_argument_field_offset) &&
+ (value.ptr() != Object::sentinel().ptr())) {
+ field ^= field_map.At(offset >> kCompressedWordSizeLog2);
+ ASSERT(!field.IsNull());
+ ASSERT(field.HostOffset() == offset);
+ field.RecordStore(value);
+ }
+#endif
}
}
}
diff --git a/runtime/vm/port.cc b/runtime/vm/port.cc
index a671373..51029ae 100644
--- a/runtime/vm/port.cc
+++ b/runtime/vm/port.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "include/dart_api.h"
#include "platform/utils.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
@@ -40,6 +41,8 @@
Dart_Port PortMap::AllocatePort() {
Dart_Port result;
+ ASSERT(mutex_->IsOwnedByCurrentThread());
+
// Keep getting new values while we have an illegal port number or the port
// number is already in use.
do {
@@ -68,6 +71,9 @@
void PortMap::SetPortState(Dart_Port port, PortState state) {
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return;
+ }
auto it = ports_->TryLookup(port);
ASSERT(it != ports_->end());
@@ -93,6 +99,10 @@
Dart_Port PortMap::CreatePort(MessageHandler* handler) {
ASSERT(handler != NULL);
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return ILLEGAL_PORT;
+ }
+
#if defined(DEBUG)
handler->CheckAccess();
#endif
@@ -126,6 +136,9 @@
MessageHandler* handler = NULL;
{
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return false;
+ }
auto it = ports_->TryLookup(port);
if (it == ports_->end()) {
return false;
@@ -165,6 +178,9 @@
void PortMap::ClosePorts(MessageHandler* handler) {
{
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return;
+ }
// The MessageHandler::ports_ is only accessed by [PortMap], it is guarded
// by the [PortMap::mutex_] we already hold.
for (auto isolate_it = handler->ports_.begin();
@@ -189,6 +205,9 @@
bool PortMap::PostMessage(std::unique_ptr<Message> message,
bool before_events) {
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return false;
+ }
auto it = ports_->TryLookup(message->dest_port());
if (it == ports_->end()) {
// Ownership of external data remains with the poster.
@@ -203,6 +222,9 @@
bool PortMap::IsLocalPort(Dart_Port id) {
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return false;
+ }
auto it = ports_->TryLookup(id);
if (it == ports_->end()) {
// Port does not exist.
@@ -216,6 +238,9 @@
bool PortMap::IsLivePort(Dart_Port id) {
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return false;
+ }
auto it = ports_->TryLookup(id);
if (it == ports_->end()) {
// Port does not exist.
@@ -228,6 +253,9 @@
Isolate* PortMap::GetIsolate(Dart_Port id) {
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return nullptr;
+ }
auto it = ports_->TryLookup(id);
if (it == ports_->end()) {
// Port does not exist.
@@ -241,6 +269,9 @@
bool PortMap::IsReceiverInThisIsolateGroup(Dart_Port receiver,
IsolateGroup* group) {
MutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return false;
+ }
auto it = ports_->TryLookup(receiver);
if (it == ports_->end()) return false;
auto isolate = (*it).handler->isolate();
@@ -249,7 +280,6 @@
}
void PortMap::Init() {
- // TODO(bkonyi): don't keep ports_ after Dart_Cleanup.
if (mutex_ == NULL) {
mutex_ = new Mutex();
}
@@ -276,11 +306,12 @@
}
ports_->Rebalance();
+ // Grab the mutex and delete the port set.
+ MutexLocker ml(mutex_);
delete prng_;
prng_ = NULL;
- // TODO(bkonyi): find out why deleting map_ sometimes causes crashes.
- // delete ports_;
- // ports_ = nullptr;
+ delete ports_;
+ ports_ = nullptr;
}
void PortMap::PrintPortsForMessageHandler(MessageHandler* handler,
@@ -292,6 +323,9 @@
{
JSONArray ports(&jsobj, "ports");
SafepointMutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return;
+ }
for (auto& entry : *ports_) {
if (entry.handler == handler) {
if (entry.state == kLivePort) {
@@ -309,6 +343,9 @@
void PortMap::DebugDumpForMessageHandler(MessageHandler* handler) {
SafepointMutexLocker ml(mutex_);
+ if (ports_ == nullptr) {
+ return;
+ }
Object& msg_handler = Object::Handle();
for (auto& entry : *ports_) {
if (entry.handler == handler) {
diff --git a/runtime/vm/zone.cc b/runtime/vm/zone.cc
index 1679e2c..744c8a5 100644
--- a/runtime/vm/zone.cc
+++ b/runtime/vm/zone.cc
@@ -163,23 +163,23 @@
// is created within a new thread or ApiNativeScope when calculating high
// watermarks or memory consumption.
Zone::Zone()
- : initial_buffer_(buffer_, kInitialChunkSize),
- position_(initial_buffer_.start()),
- limit_(initial_buffer_.end()),
+ : canary_(kCanary),
+ position_(reinterpret_cast<uword>(&buffer_)),
+ limit_(position_ + kInitialChunkSize),
head_(NULL),
large_segments_(NULL),
- handles_(),
- previous_(NULL) {
+ previous_(NULL),
+ handles_() {
ASSERT(Utils::IsAligned(position_, kAlignment));
Segment::IncrementMemoryCapacity(kInitialChunkSize);
#ifdef DEBUG
// Zap the entire initial buffer.
- memset(initial_buffer_.pointer(), kZapUninitializedByte,
- initial_buffer_.size());
+ memset(&buffer_, kZapUninitializedByte, kInitialChunkSize);
#endif
}
Zone::~Zone() {
+ ASSERT(canary_ == kCanary);
if (FLAG_trace_zones) {
DumpZoneSizes();
}
@@ -198,10 +198,10 @@
}
// Reset zone state.
#ifdef DEBUG
- memset(initial_buffer_.pointer(), kZapDeletedByte, initial_buffer_.size());
+ memset(&buffer_, kZapDeletedByte, kInitialChunkSize);
#endif
- position_ = initial_buffer_.start();
- limit_ = initial_buffer_.end();
+ position_ = reinterpret_cast<uword>(&buffer_);
+ limit_ = position_ + kInitialChunkSize;
small_segment_capacity_ = 0;
head_ = NULL;
large_segments_ = NULL;
@@ -215,9 +215,9 @@
size += s->size();
}
if (head_ == NULL) {
- return size + (position_ - initial_buffer_.start());
+ return size + (position_ - reinterpret_cast<uword>(&buffer_));
}
- size += initial_buffer_.size();
+ size += kInitialChunkSize;
for (Segment* s = head_->next(); s != NULL; s = s->next()) {
size += s->size();
}
@@ -230,9 +230,9 @@
size += s->size();
}
if (head_ == NULL) {
- return size + initial_buffer_.size();
+ return size + kInitialChunkSize;
}
- size += initial_buffer_.size();
+ size += kInitialChunkSize;
for (Segment* s = head_; s != NULL; s = s->next()) {
size += s->size();
}
diff --git a/runtime/vm/zone.h b/runtime/vm/zone.h
index cb45741..e4a0030 100644
--- a/runtime/vm/zone.h
+++ b/runtime/vm/zone.h
@@ -141,13 +141,9 @@
template <class ElementType>
static inline void CheckLength(intptr_t len);
- // This buffer is used for allocation before any segments.
- // This would act as the initial stack allocated chunk so that we don't
- // end up calling malloc/free on zone scopes that allocate less than
- // kChunkSize
- COMPILE_ASSERT(kAlignment <= 8);
- ALIGN8 uint8_t buffer_[kInitialChunkSize];
- MemoryRegion initial_buffer_;
+ // Guard against `new (zone) DoesNotExtendZoneAllocated()`.
+ static constexpr uint64_t kCanary = 0x656e6f7a74726164ull; // "dartzone"
+ uint64_t canary_;
// The free region in the current (head) segment or the initial buffer is
// represented as the half-open interval [position, limit). The 'position'
@@ -169,11 +165,18 @@
// List of large segments allocated in this zone; may be NULL.
Segment* large_segments_;
+ // Used for chaining zones in order to allow unwinding of stacks.
+ Zone* previous_;
+
// Structure for managing handles allocation.
VMHandles handles_;
- // Used for chaining zones in order to allow unwinding of stacks.
- Zone* previous_;
+ // This buffer is used for allocation before any segments.
+ // This would act as the initial stack allocated chunk so that we don't
+ // end up calling malloc/free on zone scopes that allocate less than
+ // kChunkSize
+ COMPILE_ASSERT(kAlignment <= 8);
+ ALIGN8 uint8_t buffer_[kInitialChunkSize];
friend class StackZone;
friend class ApiZone;
@@ -274,23 +277,26 @@
intptr_t new_len) {
CheckLength<ElementType>(new_len);
const intptr_t kElementSize = sizeof(ElementType);
- uword old_end = reinterpret_cast<uword>(old_data) + (old_len * kElementSize);
- // Resize existing allocation if nothing was allocated in between...
- if (Utils::RoundUp(old_end, kAlignment) == position_) {
- uword new_end =
- reinterpret_cast<uword>(old_data) + (new_len * kElementSize);
- // ...and there is sufficient space.
- if (new_end <= limit_) {
- ASSERT(new_len >= old_len);
- position_ = Utils::RoundUp(new_end, kAlignment);
+ if (old_data != nullptr) {
+ uword old_end =
+ reinterpret_cast<uword>(old_data) + (old_len * kElementSize);
+ // Resize existing allocation if nothing was allocated in between...
+ if (Utils::RoundUp(old_end, kAlignment) == position_) {
+ uword new_end =
+ reinterpret_cast<uword>(old_data) + (new_len * kElementSize);
+ // ...and there is sufficient space.
+ if (new_end <= limit_) {
+ ASSERT(new_len >= old_len);
+ position_ = Utils::RoundUp(new_end, kAlignment);
+ return old_data;
+ }
+ }
+ if (new_len <= old_len) {
return old_data;
}
}
- if (new_len <= old_len) {
- return old_data;
- }
ElementType* new_data = Alloc<ElementType>(new_len);
- if (old_data != 0) {
+ if (old_data != nullptr) {
memmove(reinterpret_cast<void*>(new_data),
reinterpret_cast<void*>(old_data), old_len * kElementSize);
}
diff --git a/tools/VERSION b/tools/VERSION
index 3cb9897..c9d32db 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 385
+PRERELEASE 386
PRERELEASE_PATCH 0
\ No newline at end of file