Version 2.12.0-134.0.dev
Merge commit '1b160f79fed8fa1439ffff0de6a7d4b4aa843e18' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 0c72699..aff0dd8 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -408,7 +408,7 @@
"name": "mime",
"rootUri": "../third_party/pkg/mime",
"packageUri": "lib/",
- "languageVersion": "2.0"
+ "languageVersion": "2.12"
},
{
"name": "mockito",
diff --git a/DEPS b/DEPS
index cd9bba0..690a3f3 100644
--- a/DEPS
+++ b/DEPS
@@ -101,7 +101,7 @@
"dart_style_tag": "1.3.10", # Please see the note above before updating.
"chromedriver_tag": "83.0.4103.39",
- "dartdoc_rev" : "d79877d0764ce23ffea7055049f8da5dffce0308",
+ "dartdoc_rev" : "a1d86f2c992f4660ddcc09b27733396e92765d2a",
"ffi_rev": "a5d4232cd38562c75a3ed847baa340e399538028",
"fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
"file_rev": "0e09370f581ab6388d46fda4cdab66638c0171a1",
@@ -123,7 +123,7 @@
"markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
"markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
"matcher_rev": "9cae8faa7868bf3a88a7ba45eb0bd128e66ac515",
- "mime_rev": "07635f7774447503248fbc6afb3911e9000a477e",
+ "mime_rev": "c931f4bed87221beaece356494b43731445ce7b8",
"mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
"mustache_rev": "664737ecad027e6b96d0d1e627257efa0e46fcb1",
"oauth2_tag": "1.6.0",
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
index e829ce1..41f9906 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
@@ -92,16 +92,16 @@
}
for (var param in namedParams) {
- if (param.hasRequired) {
+ if (param.hasRequired || param.isRequiredNamed) {
if (sb.isNotEmpty) {
sb.write(', ');
}
var name = param.name;
sb.write('$name: ');
offset = sb.length;
- var defaultValue = _getDefaultValue(param);
- sb.write(defaultValue);
- ranges.addAll([offset, defaultValue.length]);
+ // TODO(pq): fix to use getDefaultStringParameterValue()
+ sb.write(name);
+ ranges.addAll([offset, name.length]);
}
}
@@ -275,9 +275,6 @@
return type.getDisplayString(withNullability: false);
}
-/// TODO(pq): fix to use getDefaultStringParameterValue()
-String _getDefaultValue(ParameterElement param) => 'null';
-
/// A tuple of text to insert and an (optional) location for the cursor.
class DefaultArgument {
/// The text to insert.
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index f71db6c..fa74016 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -1398,10 +1398,7 @@
@override
String get testPackageLanguageVersion => latestLanguageVersion;
- @failingTest
Future<void> test_completeFunctionCalls_requiredNamed() async {
- // TODO(dantup): Find out how we can tell this parameter is required
- // (in the completion mapping).
final content = '''
void myFunction(String a, int b, {required String c, String d = ''}) {}
@@ -1425,11 +1422,55 @@
// Ensure the snippet comes through in the expected format with the expected
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
- expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, ${2:c})'));
+ expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
expect(item.textEdit.newText, equals(item.insertText));
expect(
item.textEdit.range,
equals(rangeFromMarkers(content)),
);
}
+
+ Future<void> test_completeFunctionCalls_requiredNamed_suggestionSet() async {
+ final otherFile = join(projectFolderPath, 'lib', 'other.dart');
+ newFile(
+ otherFile,
+ content:
+ "void myFunction(String a, int b, {required String c, String d = ''}) {}",
+ );
+ final content = '''
+ main() {
+ [[myFu^]]
+ }
+ ''';
+
+ final initialAnalysis = waitForAnalysisComplete();
+ await provideConfig(
+ () => initialize(
+ textDocumentCapabilities: withCompletionItemSnippetSupport(
+ emptyTextDocumentClientCapabilities),
+ workspaceCapabilities: withApplyEditSupport(
+ withConfigurationSupport(emptyWorkspaceClientCapabilities)),
+ ),
+ {'completeFunctionCalls': true},
+ );
+ await openFile(mainFileUri, withoutMarkers(content));
+ await initialAnalysis;
+
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+ final item = res.singleWhere((c) => c.label == 'myFunction(…)');
+ // Ensure the snippet comes through in the expected format with the expected
+ // placeholders.
+ expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
+ expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
+ expect(item.textEdit, isNull);
+
+ // Ensure the item can be resolved and gets a proper TextEdit.
+ final resolved = await resolveCompletion(item);
+ expect(resolved.textEdit, isNotNull);
+ expect(resolved.textEdit.newText, equals(item.insertText));
+ expect(
+ resolved.textEdit.range,
+ equals(rangeFromMarkers(content)),
+ );
+ }
}
diff --git a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
index ff603a4..a83e323 100644
--- a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
@@ -49,8 +49,7 @@
void main() {f^}''');
await computeSuggestions();
- assertSuggestFunction('foo', 'bool',
- defaultArgListString: 'bar, baz: null');
+ assertSuggestFunction('foo', 'bool', defaultArgListString: 'bar, baz: baz');
}
Future<void> test_ArgumentList() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
index 0c7657a..571d7b2 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
@@ -70,8 +70,8 @@
await computeSuggestions();
assertSuggestFunction('foo', 'bool',
- defaultArgListString: 'bar, baz: null',
- defaultArgumentListTextRanges: [0, 3, 10, 4]);
+ defaultArgListString: 'bar, baz: baz',
+ defaultArgumentListTextRanges: [0, 3, 10, 3]);
}
Future<void> test_ArgDefaults_inherited_method_with_required_named() async {
@@ -92,7 +92,7 @@
await computeSuggestions();
assertSuggestMethod('foo', 'A', 'bool',
- defaultArgListString: 'bar, baz: null');
+ defaultArgListString: 'bar, baz: baz');
}
Future<void> test_ArgDefaults_method_with_required_named() async {
@@ -109,8 +109,8 @@
await computeSuggestions();
assertSuggestMethod('foo', 'A', 'bool',
- defaultArgListString: 'bar, baz: null',
- defaultArgumentListTextRanges: [0, 3, 10, 4]);
+ defaultArgListString: 'bar, baz: baz',
+ defaultArgumentListTextRanges: [0, 3, 10, 3]);
}
Future<void> test_ArgumentList() async {
@@ -4139,7 +4139,7 @@
}
}''');
await computeSuggestions();
- assertSuggestConstructor('A', defaultArgListString: 'bar, baz: null');
+ assertSuggestConstructor('A', defaultArgListString: 'bar, baz: baz');
}
Future<void> test_localConstructor2() async {
@@ -4194,7 +4194,7 @@
}
}''');
await computeSuggestions();
- assertSuggestConstructor('A', defaultArgListString: 'bar, baz: null');
+ assertSuggestConstructor('A', defaultArgListString: 'bar, baz: baz');
}
Future<void> test_localConstructor_shadowed() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
index aea6d40..894b3d0 100644
--- a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
@@ -104,7 +104,7 @@
await computeSuggestions();
assertSuggestMethod('foo', 'A', 'bool',
- defaultArgListString: 'bar, baz: null');
+ defaultArgListString: 'bar, baz: baz');
}
Future<void> test_ArgumentList() async {
diff --git a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
index 4a2d307..623b668 100644
--- a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
+++ b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
@@ -102,8 +102,8 @@
expect(fff.defaultArgumentListTextRanges, [0, 3, 5, 3]);
var ggg = aSet.items.singleWhere((e) => e.label == 'ggg');
- expect(ggg.defaultArgumentListString, 'bbb: null, ccc: null');
- expect(ggg.defaultArgumentListTextRanges, [5, 4, 16, 4]);
+ expect(ggg.defaultArgumentListString, 'bbb: bbb, ccc: ccc');
+ expect(ggg.defaultArgumentListTextRanges, [5, 3, 15, 3]);
}
Future<void> test_displayUri_file() async {
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index 6c1f831..1936e65 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -1689,7 +1689,7 @@
_typeProvider.typeType,
TypeState(_typeProvider.dynamicType),
);
- } else if (variableElement is FunctionTypeAliasElement) {
+ } else if (variableElement is TypeAliasElement) {
var type = variableElement.instantiate(
typeArguments: variableElement.typeParameters
.map((t) => _typeProvider.dynamicType)
diff --git a/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart b/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
index 9daaea2..377e713 100644
--- a/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
@@ -6,9 +6,11 @@
import 'package:analyzer/dart/ast/standard_ast_factory.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/error/listener.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/error/codes.dart';
+import 'package:meta/meta.dart';
/// Helper for [MethodInvocation]s into [InstanceCreationExpression] to support
/// the optional `new` and `const` feature, or [ExtensionOverride].
@@ -34,14 +36,10 @@
}
Element element = nameScope.lookup(methodName.name).getter;
if (element is ClassElement) {
- TypeName typeName = astFactory.typeName(methodName, node.typeArguments);
- ConstructorName constructorName =
- astFactory.constructorName(typeName, null, null);
- InstanceCreationExpression instanceCreationExpression =
- astFactory.instanceCreationExpression(
- null, constructorName, node.argumentList);
- NodeReplacer.replace(node, instanceCreationExpression);
- return instanceCreationExpression;
+ return _toInstanceCreation_type(
+ node: node,
+ typeIdentifier: methodName,
+ );
} else if (element is ExtensionElement) {
ExtensionOverride extensionOverride = astFactory.extensionOverride(
extensionName: methodName,
@@ -49,6 +47,12 @@
argumentList: node.argumentList);
NodeReplacer.replace(node, extensionOverride);
return extensionOverride;
+ } else if (element is TypeAliasElement &&
+ element.aliasedType is InterfaceType) {
+ return _toInstanceCreation_type(
+ node: node,
+ typeIdentifier: methodName,
+ );
}
} else if (target is SimpleIdentifier) {
// Possible cases: C.n(), p.C() or p.C<>()
@@ -58,41 +62,23 @@
}
Element element = nameScope.lookup(target.name).getter;
if (element is ClassElement) {
- // Possible case: C.n()
- var constructorElement = element.getNamedConstructor(methodName.name);
- if (constructorElement != null) {
- var typeArguments = node.typeArguments;
- if (typeArguments != null) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
- typeArguments,
- [element.name, constructorElement.name]);
- }
- TypeName typeName = astFactory.typeName(target, null);
- ConstructorName constructorName =
- astFactory.constructorName(typeName, node.operator, methodName);
- // TODO(scheglov) I think we should drop "typeArguments" below.
- InstanceCreationExpression instanceCreationExpression =
- astFactory.instanceCreationExpression(
- null, constructorName, node.argumentList,
- typeArguments: typeArguments);
- NodeReplacer.replace(node, instanceCreationExpression);
- return instanceCreationExpression;
- }
+ // class C { C.named(); }
+ // C.named()
+ return _toInstanceCreation_type_constructor(
+ node: node,
+ typeIdentifier: target,
+ constructorIdentifier: methodName,
+ classElement: element,
+ );
} else if (element is PrefixElement) {
// Possible cases: p.C() or p.C<>()
Element prefixedElement = element.scope.lookup(methodName.name).getter;
if (prefixedElement is ClassElement) {
- TypeName typeName = astFactory.typeName(
- astFactory.prefixedIdentifier(target, node.operator, methodName),
- node.typeArguments);
- ConstructorName constructorName =
- astFactory.constructorName(typeName, null, null);
- InstanceCreationExpression instanceCreationExpression =
- astFactory.instanceCreationExpression(
- null, constructorName, node.argumentList);
- NodeReplacer.replace(node, instanceCreationExpression);
- return instanceCreationExpression;
+ return _toInstanceCreation_prefix_type(
+ node: node,
+ prefixIdentifier: target,
+ typeIdentifier: methodName,
+ );
} else if (prefixedElement is ExtensionElement) {
PrefixedIdentifier extensionName =
astFactory.prefixedIdentifier(target, node.operator, methodName);
@@ -102,38 +88,143 @@
argumentList: node.argumentList);
NodeReplacer.replace(node, extensionOverride);
return extensionOverride;
+ } else if (prefixedElement is TypeAliasElement &&
+ prefixedElement.aliasedType is InterfaceType) {
+ return _toInstanceCreation_prefix_type(
+ node: node,
+ prefixIdentifier: target,
+ typeIdentifier: methodName,
+ );
+ }
+ } else if (element is TypeAliasElement) {
+ var aliasedType = element.aliasedType;
+ if (aliasedType is InterfaceType) {
+ // class C { C.named(); }
+ // typedef X = C;
+ // X.named()
+ return _toInstanceCreation_type_constructor(
+ node: node,
+ typeIdentifier: target,
+ constructorIdentifier: methodName,
+ classElement: aliasedType.element,
+ );
}
}
} else if (target is PrefixedIdentifier) {
// Possible case: p.C.n()
- Element prefixElement = nameScope.lookup(target.prefix.name).getter;
+ var prefixElement = nameScope.lookup(target.prefix.name).getter;
target.prefix.staticElement = prefixElement;
if (prefixElement is PrefixElement) {
- Element element =
- prefixElement.scope.lookup(target.identifier.name).getter;
+ var prefixedName = target.identifier.name;
+ var element = prefixElement.scope.lookup(prefixedName).getter;
if (element is ClassElement) {
- var constructorElement = element.getNamedConstructor(methodName.name);
- if (constructorElement != null) {
- var typeArguments = node.typeArguments;
- if (typeArguments != null) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode
- .WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
- typeArguments,
- [element.name, constructorElement.name]);
- }
- TypeName typeName = astFactory.typeName(target, typeArguments);
- ConstructorName constructorName =
- astFactory.constructorName(typeName, node.operator, methodName);
- InstanceCreationExpression instanceCreationExpression =
- astFactory.instanceCreationExpression(
- null, constructorName, node.argumentList);
- NodeReplacer.replace(node, instanceCreationExpression);
- return instanceCreationExpression;
+ return _instanceCreation_prefix_type_name(
+ node: node,
+ typeNameIdentifier: target,
+ constructorIdentifier: methodName,
+ classElement: element,
+ );
+ } else if (element is TypeAliasElement) {
+ var aliasedType = element.aliasedType;
+ if (aliasedType is InterfaceType) {
+ return _instanceCreation_prefix_type_name(
+ node: node,
+ typeNameIdentifier: target,
+ constructorIdentifier: methodName,
+ classElement: aliasedType.element,
+ );
}
}
}
}
return node;
}
+
+ AstNode _instanceCreation_prefix_type_name({
+ @required MethodInvocation node,
+ @required PrefixedIdentifier typeNameIdentifier,
+ @required SimpleIdentifier constructorIdentifier,
+ @required ClassElement classElement,
+ }) {
+ var constructorElement = classElement.getNamedConstructor(
+ constructorIdentifier.name,
+ );
+ if (constructorElement == null) {
+ return node;
+ }
+
+ var typeArguments = node.typeArguments;
+ if (typeArguments != null) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
+ typeArguments,
+ [classElement.name, constructorElement.name]);
+ }
+
+ var typeName = astFactory.typeName(typeNameIdentifier, typeArguments);
+ var constructorName = astFactory.constructorName(
+ typeName, node.operator, constructorIdentifier);
+ var instanceCreationExpression = astFactory.instanceCreationExpression(
+ null, constructorName, node.argumentList);
+ NodeReplacer.replace(node, instanceCreationExpression);
+ return instanceCreationExpression;
+ }
+
+ InstanceCreationExpression _toInstanceCreation_prefix_type({
+ @required MethodInvocation node,
+ @required SimpleIdentifier prefixIdentifier,
+ @required SimpleIdentifier typeIdentifier,
+ }) {
+ var typeName = astFactory.typeName(
+ astFactory.prefixedIdentifier(
+ prefixIdentifier, node.operator, typeIdentifier),
+ node.typeArguments);
+ var constructorName = astFactory.constructorName(typeName, null, null);
+ var instanceCreationExpression = astFactory.instanceCreationExpression(
+ null, constructorName, node.argumentList);
+ NodeReplacer.replace(node, instanceCreationExpression);
+ return instanceCreationExpression;
+ }
+
+ InstanceCreationExpression _toInstanceCreation_type({
+ @required MethodInvocation node,
+ @required SimpleIdentifier typeIdentifier,
+ }) {
+ var typeName = astFactory.typeName(typeIdentifier, node.typeArguments);
+ var constructorName = astFactory.constructorName(typeName, null, null);
+ var instanceCreationExpression = astFactory.instanceCreationExpression(
+ null, constructorName, node.argumentList);
+ NodeReplacer.replace(node, instanceCreationExpression);
+ return instanceCreationExpression;
+ }
+
+ AstNode _toInstanceCreation_type_constructor({
+ @required MethodInvocation node,
+ @required SimpleIdentifier typeIdentifier,
+ @required SimpleIdentifier constructorIdentifier,
+ @required ClassElement classElement,
+ }) {
+ var name = constructorIdentifier.name;
+ var constructorElement = classElement.getNamedConstructor(name);
+ if (constructorElement == null) {
+ return node;
+ }
+
+ var typeArguments = node.typeArguments;
+ if (typeArguments != null) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
+ typeArguments,
+ [classElement.name, constructorElement.name]);
+ }
+ var typeName = astFactory.typeName(typeIdentifier, null);
+ var constructorName = astFactory.constructorName(
+ typeName, node.operator, constructorIdentifier);
+ // TODO(scheglov) I think we should drop "typeArguments" below.
+ var instanceCreationExpression = astFactory.instanceCreationExpression(
+ null, constructorName, node.argumentList,
+ typeArguments: typeArguments);
+ NodeReplacer.replace(node, instanceCreationExpression);
+ return instanceCreationExpression;
+ }
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
index c077118..eef2897 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
@@ -196,12 +196,6 @@
typeArguments: typeArguments,
nullabilitySuffix: nullability,
);
- } else if (_isInstanceCreation(node)) {
- _ErrorHelper(errorReporter).reportNewWithNonType(node);
- return dynamicType;
- } else if (element is DynamicElementImpl) {
- _buildTypeArguments(node, 0);
- return DynamicTypeImpl.instance;
} else if (element is TypeAliasElement) {
var typeArguments = _buildTypeArguments(
node,
@@ -212,7 +206,13 @@
nullabilitySuffix: nullability,
);
type = typeSystem.toLegacyType(type);
- return type;
+ return _verifyTypeAliasForContext(node, type);
+ } else if (_isInstanceCreation(node)) {
+ _ErrorHelper(errorReporter).reportNewWithNonType(node);
+ return dynamicType;
+ } else if (element is DynamicElementImpl) {
+ _buildTypeArguments(node, 0);
+ return DynamicTypeImpl.instance;
} else if (element is NeverElementImpl) {
_buildTypeArguments(node, 0);
return _instantiateElementNever(nullability);
@@ -244,16 +244,17 @@
classElement: element,
nullabilitySuffix: nullability,
);
+ } else if (element is TypeAliasElement) {
+ var type = typeSystem.instantiateToBounds2(
+ typeAliasElement: element,
+ nullabilitySuffix: nullability,
+ );
+ return _verifyTypeAliasForContext(node, type);
} else if (_isInstanceCreation(node)) {
_ErrorHelper(errorReporter).reportNewWithNonType(node);
return dynamicType;
} else if (element is DynamicElementImpl) {
return DynamicTypeImpl.instance;
- } else if (element is TypeAliasElement) {
- return typeSystem.instantiateToBounds2(
- typeAliasElement: element,
- nullabilitySuffix: nullability,
- );
} else if (element is NeverElementImpl) {
return _instantiateElementNever(nullability);
} else if (element is TypeParameterElement) {
@@ -366,6 +367,14 @@
}
}
+ DartType _verifyTypeAliasForContext(TypeName node, DartType type) {
+ if (type is! InterfaceType && _isInstanceCreation(node)) {
+ _ErrorHelper(errorReporter).reportNewWithNonType(node);
+ return dynamicType;
+ }
+ return type;
+ }
+
static bool _isInstanceCreation(TypeName node) {
return node.parent is ConstructorName &&
node.parent.parent is InstanceCreationExpression;
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 45b860a..23bc84d 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -1295,6 +1295,9 @@
if (expressionMap.isNotEmpty) {
Declaration parent = expression.thisOrAncestorMatching(
(e) => e is FunctionDeclaration || e is MethodDeclaration);
+ if (parent == null) {
+ return;
+ }
for (var entry in expressionMap.entries) {
_errorReporter.reportErrorForNode(
HintCode.RETURN_OF_DO_NOT_STORE,
@@ -1480,6 +1483,11 @@
addTo: expressions);
_getSubExpressionsMarkedDoNotStore(expression.rightOperand,
addTo: expressions);
+ } else if (expression is FunctionExpression) {
+ var body = expression.body;
+ if (body is ExpressionFunctionBody) {
+ _getSubExpressionsMarkedDoNotStore(body.expression, addTo: expressions);
+ }
}
if (element is PropertyAccessorElement && element.isSynthetic) {
element = (element as PropertyAccessorElement).variable;
diff --git a/pkg/analyzer/lib/src/services/available_declarations.dart b/pkg/analyzer/lib/src/services/available_declarations.dart
index 5227a27..4edfaae 100644
--- a/pkg/analyzer/lib/src/services/available_declarations.dart
+++ b/pkg/analyzer/lib/src/services/available_declarations.dart
@@ -1828,32 +1828,22 @@
var buffer = StringBuffer();
var ranges = <int>[];
for (var parameter in parameters.parameters) {
- if (parameter.isRequired) {
+ if (parameter.isRequired ||
+ (parameter.isNamed && _hasRequiredAnnotation(parameter))) {
if (buffer.isNotEmpty) {
buffer.write(', ');
}
+
if (parameter.isNamed) {
buffer.write(parameter.identifier.name);
buffer.write(': ');
}
+
var valueOffset = buffer.length;
buffer.write(parameter.identifier.name);
var valueLength = buffer.length - valueOffset;
ranges.add(valueOffset);
ranges.add(valueLength);
- } else if (parameter.isNamed && _hasRequiredAnnotation(parameter)) {
- if (buffer.isNotEmpty) {
- buffer.write(', ');
- }
- buffer.write(parameter.identifier.name);
- buffer.write(': ');
-
- var valueOffset = buffer.length;
- buffer.write('null');
- var valueLength = buffer.length - valueOffset;
-
- ranges.add(valueOffset);
- ranges.add(valueLength);
}
}
if (buffer.isEmpty) return null;
diff --git a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
index a51644b..4225019 100644
--- a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
@@ -15,11 +15,17 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AstRewriteMethodInvocationTest);
+ defineReflectiveTests(
+ AstRewriteMethodInvocationWithNonFunctionTypeAliasesTest,
+ );
});
}
@reflectiveTest
-class AstRewriteMethodInvocationTest extends PubPackageResolutionTest {
+class AstRewriteMethodInvocationTest extends PubPackageResolutionTest
+ with AstRewriteMethodInvocationTestCases {}
+
+mixin AstRewriteMethodInvocationTestCases on PubPackageResolutionTest {
test_targetNull_cascade() async {
await assertNoErrorsInCode(r'''
class A {
@@ -379,3 +385,107 @@
expect(argumentStrings, expectedArguments);
}
}
+
+@reflectiveTest
+class AstRewriteMethodInvocationWithNonFunctionTypeAliasesTest
+ extends PubPackageResolutionTest
+ with WithNonFunctionTypeAliasesMixin, AstRewriteMethodInvocationTestCases {
+ test_targetNull_typeAlias_interfaceType() async {
+ await assertNoErrorsInCode(r'''
+class A<T, U> {
+ A(int _);
+}
+
+typedef X<T, U> = A<T, U>;
+
+void f() {
+ X<int, String>(0);
+}
+''');
+
+ var creation = findNode.instanceCreation('X<int, String>(0);');
+ assertInstanceCreation(
+ creation,
+ findElement.class_('A'),
+ 'A<int, String>',
+ expectedConstructorMember: true,
+ expectedSubstitution: {'T': 'int', 'U': 'String'},
+ expectedTypeNameElement: findElement.typeAlias('X'),
+ );
+ _assertArgumentList(creation.argumentList, ['0']);
+ }
+
+ test_targetNull_typeAlias_Never() async {
+ await assertErrorsInCode(r'''
+typedef X = Never;
+
+void f() {
+ X(0);
+}
+''', [
+ error(CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION, 33, 1),
+ ]);
+
+ // Not rewritten.
+ findNode.methodInvocation('X(0)');
+ }
+
+ test_targetPrefixedIdentifier_typeAlias_interfaceType_constructor() async {
+ newFile('$testPackageLibPath/a.dart', content: r'''
+class A<T> {
+ A.named(T a);
+}
+
+typedef X<T> = A<T>;
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+void f() {
+ prefix.X.named(0);
+}
+''');
+
+ var importFind = findElement.importFind('package:test/a.dart');
+
+ var creation = findNode.instanceCreation('X.named(0);');
+ assertInstanceCreation(
+ creation,
+ importFind.class_('A'),
+ 'A<int>',
+ constructorName: 'named',
+ expectedConstructorMember: true,
+ expectedSubstitution: {'T': 'int'},
+ expectedPrefix: findElement.prefix('prefix'),
+ expectedTypeNameElement: importFind.typeAlias('X'),
+ );
+ _assertArgumentList(creation.argumentList, ['0']);
+ }
+
+ test_targetSimpleIdentifier_typeAlias_interfaceType_constructor() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ A.named(T a);
+}
+
+typedef X<T> = A<T>;
+
+void f() {
+ X.named(0);
+}
+''');
+
+ var creation = findNode.instanceCreation('X.named(0);');
+ assertInstanceCreation(
+ creation,
+ findElement.class_('A'),
+ 'A<int>',
+ constructorName: 'named',
+ expectedConstructorMember: true,
+ expectedSubstitution: {'T': 'int'},
+ expectedTypeNameElement: findElement.typeAlias('X'),
+ );
+ _assertArgumentList(creation.argumentList, ['0']);
+ }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 5e7e81c..31bed01 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -400,12 +400,16 @@
}
}
- void assertInstanceCreation(InstanceCreationExpression creation,
- ClassElement expectedClassElement, String expectedType,
- {String constructorName,
- bool expectedConstructorMember = false,
- Map<String, String> expectedSubstitution,
- PrefixElement expectedPrefix}) {
+ void assertInstanceCreation(
+ InstanceCreationExpression creation,
+ ClassElement expectedClassElement,
+ String expectedType, {
+ String constructorName,
+ bool expectedConstructorMember = false,
+ Map<String, String> expectedSubstitution,
+ PrefixElement expectedPrefix,
+ Element expectedTypeNameElement,
+ }) {
String expectedClassName = expectedClassElement.name;
ConstructorElement expectedConstructorElement;
@@ -444,7 +448,8 @@
assertType(creation, expectedType);
var typeName = creation.constructorName.type;
- assertTypeName(typeName, expectedClassElement, expectedType,
+ expectedTypeNameElement ??= expectedClassElement;
+ assertTypeName(typeName, expectedTypeNameElement, expectedType,
expectedPrefix: expectedPrefix);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index a58eac5..146b2a1 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -291,6 +291,61 @@
@reflectiveTest
class TypeNameResolutionWithNonFunctionTypeAliasesTest
extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+ test_typeAlias_asInstanceCreation_explicitNew_typeArguments_interfaceType_none() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {}
+
+typedef X<T> = A<T>;
+
+void f() {
+ new X<int>();
+}
+''');
+
+ assertTypeName(
+ findNode.typeName('X<int>()'),
+ findElement.typeAlias('X'),
+ 'A<int>',
+ );
+ }
+
+ @FailingTest(reason: 'We attempt to do type inference on A')
+ test_typeAlias_asInstanceCreation_implicitNew_toBounds_noTypeParameters_interfaceType_none() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {}
+
+typedef X = A<int>;
+
+void f() {
+ X();
+}
+''');
+
+ assertTypeName(
+ findNode.typeName('X()'),
+ findElement.typeAlias('X'),
+ 'A<int>',
+ );
+ }
+
+ test_typeAlias_asInstanceCreation_implicitNew_typeArguments_interfaceType_none() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {}
+
+typedef X<T> = A<T>;
+
+void f() {
+ X<int>();
+}
+''');
+
+ assertTypeName(
+ findNode.typeName('X<int>()'),
+ findElement.typeAlias('X'),
+ 'A<int>',
+ );
+ }
+
test_typeAlias_asParameter_Never_none() async {
await assertNoErrorsInCode(r'''
typedef X = Never;
diff --git a/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart b/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart
index e72359b..3266124 100644
--- a/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart
@@ -193,6 +193,21 @@
]);
}
+ test_topLevelVariable_assignment_functionExpression() async {
+ await assertErrorsInCode('''
+import 'package:meta/meta.dart';
+
+@doNotStore
+String _v = '';
+
+var c = ()=> _v;
+
+String v = c();
+''', [
+ error(HintCode.ASSIGNMENT_OF_DO_NOT_STORE, 76, 2),
+ ]);
+ }
+
test_topLevelVariable_assignment_getter() async {
await assertErrorsInCode('''
import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
index 8e28cd4..79c1c58 100644
--- a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
@@ -10,6 +10,9 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstInitializedWithNonConstantValueTest);
+ defineReflectiveTests(
+ ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest,
+ );
});
}
@@ -103,3 +106,20 @@
]);
}
}
+
+@reflectiveTest
+class ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest
+ extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+ test_typeLiteral_interfaceType() async {
+ await assertNoErrorsInCode(r'''
+const a = int;
+''');
+ }
+
+ test_typeLiteral_typeAlias_interfaceType() async {
+ await assertNoErrorsInCode(r'''
+typedef A = int;
+const a = A;
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart b/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart
index c747e6a..c8f61f5 100644
--- a/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart
@@ -21,6 +21,22 @@
writeTestPackageConfigWithMeta();
}
+ test_returnFromClosureInFunction() async {
+ await assertErrorsInCode('''
+import 'package:meta/meta.dart';
+
+@doNotStore
+String _v = '';
+
+String f() {
+ var v = () => _v;
+ return v();
+}
+''', [
+ error(HintCode.RETURN_OF_DO_NOT_STORE, 92, 2),
+ ]);
+ }
+
test_returnFromFunction() async {
await assertErrorsInCode('''
import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/src/services/available_declarations_test.dart b/pkg/analyzer/test/src/services/available_declarations_test.dart
index 3e2aa12..b5304ba 100644
--- a/pkg/analyzer/test/src/services/available_declarations_test.dart
+++ b/pkg/analyzer/test/src/services/available_declarations_test.dart
@@ -1934,8 +1934,8 @@
_getDeclaration(library.declarations, 'd'),
'd',
DeclarationKind.FUNCTION,
- defaultArgumentListString: 'a, c: null, d: null',
- defaultArgumentListTextRanges: [0, 1, 6, 4, 15, 4],
+ defaultArgumentListString: 'a, c: c, d: d',
+ defaultArgumentListTextRanges: [0, 1, 6, 1, 12, 1],
parameters: '(int a, {int b, @required int c, @required int d, int e})',
parameterNames: ['a', 'b', 'c', 'd', 'e'],
parameterTypes: ['int', 'int', 'int', 'int', 'int'],
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
index 71b5b58..cea8688 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
@@ -46,16 +46,15 @@
}
for (var param in namedParams) {
- if (param.hasRequired) {
+ if (param.hasRequired || param.isRequiredNamed) {
if (buffer.isNotEmpty) {
buffer.write(', ');
}
var name = param.name;
buffer.write('$name: ');
offset = buffer.length;
- var defaultValue = 'null'; // originally _getDefaultValue(param)
- buffer.write(defaultValue);
- ranges.addAll([offset, defaultValue.length]);
+ buffer.write(name);
+ ranges.addAll([offset, name.length]);
}
}
diff --git a/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart b/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart
index 49d103d..2b63605 100644
--- a/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart
+++ b/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart
@@ -53,7 +53,7 @@
await computeSuggestions();
assertSuggestMethod('foo', 'A', 'bool',
- defaultArgListString: 'bar, baz: null');
+ defaultArgListString: 'bar, baz: baz');
}
Future<void> test_AwaitExpression_inherited() async {
diff --git a/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart b/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart
index c268c4e..59a19c1 100644
--- a/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart
+++ b/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart
@@ -114,7 +114,7 @@
await computeSuggestions();
assertSuggestMethod('foo', 'A', 'bool',
- defaultArgListString: 'bar, baz: null');
+ defaultArgListString: 'bar, baz: baz');
}
Future<void> test_ArgumentList() async {
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 1c7d0ed..c93d471 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -117,6 +117,7 @@
static const String soundNullSafety = '--sound-null-safety';
static const String noSoundNullSafety = '--no-sound-null-safety';
+ static const String mergeFragmentsThreshold = '--merge-fragments-threshold';
/// Flag for a combination of flags for 'production' mode.
static const String benchmarkingProduction = '--benchmarking-production';
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 11b8c5d..5a69e20 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -559,6 +559,7 @@
new OptionHandler(Flags.experimentUnreachableMethodsThrow, passThrough),
new OptionHandler(Flags.experimentCallInstrumentation, passThrough),
new OptionHandler(Flags.experimentNewRti, ignoreOption),
+ new OptionHandler('${Flags.mergeFragmentsThreshold}=.+', passThrough),
// The following three options must come last.
new OptionHandler('-D.+=.*', addInEnvironment),
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index 364fe9a..829e2c5 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -84,6 +84,13 @@
Set<ImportEntity> get importsForTesting => _imports;
+ void merge(OutputUnit that) {
+ assert(this != that);
+ // We don't currently support merging code into the main output unit.
+ assert(!isMainOutput);
+ this._imports.addAll(that._imports);
+ }
+
@override
String toString() => "OutputUnit($name, $_imports)";
}
diff --git a/pkg/compiler/lib/src/js/size_estimator.dart b/pkg/compiler/lib/src/js/size_estimator.dart
index 60d0354..347c146 100644
--- a/pkg/compiler/lib/src/js/size_estimator.dart
+++ b/pkg/compiler/lib/src/js/size_estimator.dart
@@ -12,6 +12,13 @@
import '../js_backend/type_reference.dart';
import '../js_emitter/metadata_collector.dart';
+/// Estimates the size of the Javascript AST represented by the provided [Node].
+int estimateSize(Node node) {
+ var estimator = SizeEstimator();
+ estimator.visit(node);
+ return estimator.charCount;
+}
+
/// [SizeEstimator] is a [NodeVisitor] designed to produce a consistent size
/// estimate for a given JavaScript AST. [SizeEstimator] trades accuracy for
/// stability and performance. In addition, [SizeEstimator] assumes we will emit
@@ -55,12 +62,20 @@
} else if (node is StringReference) {
// Worst case we have to inline the string so size of string + 2 bytes for
// quotes.
- return "'${node.constant}'";
+ return "'${node.constant.toDartString()}'";
} else {
throw UnsupportedError('$node type is not supported');
}
}
+ String literalStringToString(LiteralString node) {
+ if (node.isFinalized) {
+ return node.value;
+ } else {
+ return sizeEstimate(node);
+ }
+ }
+
/// Always emit a newline, even under `enableMinification`.
void forceLine() {
out('\n'); // '\n'
@@ -757,7 +772,7 @@
newInForInit: inForInit, newAtStatementBegin: atStatementBegin);
Node selector = access.selector;
if (selector is LiteralString) {
- String fieldWithQuotes = selector.value;
+ String fieldWithQuotes = literalStringToString(selector);
if (isValidJavaScriptId(fieldWithQuotes)) {
if (access.receiver is LiteralNumber) {
// We can eliminate the space in some cases, but for simplicity we
@@ -846,11 +861,7 @@
@override
void visitLiteralString(LiteralString node) {
- if (node.isFinalized) {
- out(node.value); // '${node.value}'
- } else {
- out(sizeEstimate(node));
- }
+ out(literalStringToString(node));
}
@override
@@ -943,7 +954,7 @@
void visitProperty(Property node) {
Node name = node.name;
if (name is LiteralString) {
- String text = name.value;
+ String text = literalStringToString(name);
if (isValidJavaScriptId(text)) {
// '${text.substring(1, text.length - 1)}
out('${text.substring(1, text.length - 1)}');
@@ -1034,9 +1045,3 @@
visit(node.expression);
}
}
-
-int EstimateSize(Node node) {
- var estimator = SizeEstimator();
- estimator.visit(node);
- return estimator.charCount;
-}
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index b412bd7..fb72825 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -20,6 +20,7 @@
import '../world.dart' show JClosedWorld;
import 'program_builder/program_builder.dart';
import 'startup_emitter/emitter.dart' as startup_js_emitter;
+import 'startup_emitter/fragment_merger.dart' as fragment_merger;
import 'metadata_collector.dart' show MetadataCollector;
import 'model.dart';
@@ -206,6 +207,8 @@
abstract class Emitter implements ModularEmitter {
Program get programForTesting;
+ List<fragment_merger.PreFragment> get preDeferredFragmentsForTesting;
+
/// Uses the [programBuilder] to generate a model of the program, emits
/// the program, and returns the size of the generated output.
int emitProgram(ProgramBuilder programBuilder, CodegenWorld codegenWorld);
diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
index 6fac2f9..db10350 100644
--- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
+++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
@@ -115,8 +115,7 @@
}
/// A map used to canonicalize the entries of metadata.
- Map<OutputUnit, Map<String, BoundMetadataEntry>> _metadataMap =
- <OutputUnit, Map<String, BoundMetadataEntry>>{};
+ Map<OutputUnit, Map<String, List<BoundMetadataEntry>>> _metadataMap = {};
/// A map with a token for a lists of JS expressions, one token for each
/// output unit. Once finalized, the entries represent types including
@@ -129,8 +128,35 @@
}
/// A map used to canonicalize the entries of types.
- Map<OutputUnit, Map<DartType, BoundMetadataEntry>> _typesMap =
- <OutputUnit, Map<DartType, BoundMetadataEntry>>{};
+ Map<OutputUnit, Map<DartType, List<BoundMetadataEntry>>> _typesMap = {};
+
+ void mergeOutputUnitMetadata(OutputUnit target, OutputUnit source) {
+ assert(target != source);
+
+ // Merge _metadataMap
+ var sourceMetadataMap = _metadataMap[source];
+ if (sourceMetadataMap != null) {
+ var targetMetadataMap =
+ _metadataMap[target] ??= Map<String, List<BoundMetadataEntry>>();
+ _metadataMap.remove(source);
+ sourceMetadataMap.forEach((str, entries) {
+ var targetMetadataMapList = targetMetadataMap[str] ??= [];
+ targetMetadataMapList.addAll(entries);
+ });
+ }
+
+ // Merge _typesMap
+ var sourceTypesMap = _typesMap[source];
+ if (sourceTypesMap != null) {
+ var targetTypesMap =
+ _typesMap[target] ??= Map<DartType, List<BoundMetadataEntry>>();
+ _typesMap.remove(source);
+ sourceTypesMap.forEach((type, entries) {
+ var targetTypesMapList = targetTypesMap[type] ??= [];
+ targetTypesMapList.addAll(entries);
+ });
+ }
+ }
MetadataCollector(this._options, this.reporter, this._emitter,
this._rtiRecipeEncoder, this._elementEnvironment);
@@ -166,10 +192,9 @@
String printed = jsAst.prettyPrint(node,
enableMinification: _options.enableMinification,
renamerForNames: nameToKey);
- _metadataMap[outputUnit] ??= new Map<String, BoundMetadataEntry>();
- return _metadataMap[outputUnit].putIfAbsent(printed, () {
- return new BoundMetadataEntry(node);
- });
+ final submap = _metadataMap[outputUnit] ??= {};
+ final entries = submap[printed] ??= [BoundMetadataEntry(node)];
+ return entries.single;
}
jsAst.Expression _computeTypeRepresentationNewRti(DartType type) {
@@ -178,10 +203,20 @@
}
jsAst.Expression addTypeInOutputUnit(DartType type, OutputUnit outputUnit) {
- _typesMap[outputUnit] ??= new Map<DartType, BoundMetadataEntry>();
- return _typesMap[outputUnit].putIfAbsent(type, () {
- return new BoundMetadataEntry(_computeTypeRepresentationNewRti(type));
- });
+ _typesMap[outputUnit] ??= Map<DartType, List<BoundMetadataEntry>>();
+ BoundMetadataEntry metadataEntry;
+
+ // See comment for _addGlobalMetadata.
+ if (_typesMap[outputUnit].containsKey(type)) {
+ metadataEntry = _typesMap[outputUnit][type].single;
+ } else {
+ _typesMap[outputUnit].putIfAbsent(type, () {
+ metadataEntry =
+ BoundMetadataEntry(_computeTypeRepresentationNewRti(type));
+ return [metadataEntry];
+ });
+ }
+ return metadataEntry;
}
@override
@@ -194,9 +229,13 @@
.forEach(counter.countTokens);
}
- jsAst.ArrayInitializer finalizeMap(Map<dynamic, BoundMetadataEntry> map) {
- bool isUsed(BoundMetadataEntry entry) => entry.isUsed;
- List<BoundMetadataEntry> entries = map.values.where(isUsed).toList();
+ jsAst.ArrayInitializer finalizeMap(
+ Map<dynamic, List<BoundMetadataEntry>> map) {
+ List<BoundMetadataEntry> entries = [
+ for (var entriesList in map.values)
+ for (var entry in entriesList)
+ if (entry.isUsed) entry
+ ];
entries.sort();
// TODO(herhut): Bucket entries by index length and use a stable
@@ -222,9 +261,9 @@
});
_typesTokens.forEach((OutputUnit outputUnit, _MetadataList token) {
- Map typesMap = _typesMap[outputUnit];
+ Map<DartType, List<BoundMetadataEntry>> typesMap = _typesMap[outputUnit];
if (typesMap != null) {
- countTokensInTypes(typesMap.values);
+ typesMap.values.forEach(countTokensInTypes);
token.setExpression(finalizeMap(typesMap));
} else {
token.setExpression(new jsAst.ArrayInitializer([]));
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index 7479339..fb2ffc6 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -42,6 +42,10 @@
assert(outputContainsConstantList != null);
}
+ void mergeOutputUnitMetadata(OutputUnit target, OutputUnit source) {
+ _metadataCollector.mergeOutputUnitMetadata(target, source);
+ }
+
/// Accessor for the list of metadata entries for a given [OutputUnit].
///
/// There is one list for each output unit. The list belonging to the main
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
index b2b924b..acf7e3c 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -24,6 +24,7 @@
import '../model.dart';
import '../native_emitter.dart';
import '../program_builder/program_builder.dart' show ProgramBuilder;
+import 'fragment_merger.dart';
import 'model_emitter.dart';
abstract class ModularEmitterBase implements ModularEmitter {
@@ -156,6 +157,9 @@
@override
Program programForTesting;
+ @override
+ List<PreFragment> preDeferredFragmentsForTesting;
+
EmitterImpl(
CompilerOptions options,
this._reporter,
@@ -196,7 +200,12 @@
programForTesting = program;
}
return _task.measureSubtask('emit program', () {
- return _emitter.emitProgram(program, codegenWorld);
+ var size = _emitter.emitProgram(program, codegenWorld);
+ if (retainDataForTesting) {
+ preDeferredFragmentsForTesting =
+ _emitter.preDeferredFragmentsForTesting;
+ }
+ return size;
});
}
@@ -238,11 +247,9 @@
@override
int generatedSize(OutputUnit unit) {
- if (_emitter.omittedFragments.any((f) => f.outputUnit == unit)) {
+ if (_emitter.omittedOutputUnits.contains(unit)) {
return 0;
}
- Fragment key = _emitter.outputBuffers.keys
- .firstWhere((Fragment fragment) => fragment.outputUnit == unit);
- return _emitter.outputBuffers[key].length;
+ return _emitter.emittedOutputBuffers[unit].length;
}
}
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 597f756..dd7bd3a 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -690,6 +690,35 @@
if (library != null) _dumpInfoTask.registerEntityAst(library, code);
}
+ PreFragment emitPreFragment(DeferredFragment fragment, bool estimateSize) {
+ var classPrototypes = emitPrototypes(fragment, includeClosures: false);
+ var closurePrototypes = emitPrototypes(fragment, includeClosures: true);
+ var inheritance = emitInheritance(fragment);
+ var methodAliases = emitInstanceMethodAliases(fragment);
+ var tearOffs = emitInstallTearOffs(fragment);
+ var constants = emitConstants(fragment);
+ var typeRules = emitTypeRules(fragment);
+ var variances = emitVariances(fragment);
+ var staticNonFinalFields = emitStaticNonFinalFields(fragment);
+ var lazyInitializers = emitLazilyInitializedStatics(fragment);
+ // TODO(floitsch): only call emitNativeSupport if we need native.
+ var nativeSupport = emitNativeSupport(fragment);
+ return PreFragment(
+ fragment,
+ classPrototypes,
+ closurePrototypes,
+ inheritance,
+ methodAliases,
+ tearOffs,
+ constants,
+ typeRules,
+ variances,
+ staticNonFinalFields,
+ lazyInitializers,
+ nativeSupport,
+ estimateSize);
+ }
+
js.Statement emitMainFragment(
Program program, DeferredLoadingState deferredLoadingState) {
MainFragment fragment = program.fragments.first;
@@ -699,8 +728,8 @@
String softDeferredId = "softDeferred${new Random().nextInt(0x7FFFFFFF)}";
- HolderCode holderCode =
- emitHolders(program.holders, fragment, initializeEmptyHolders: true);
+ HolderCode holderCode = emitHolders(program.holders, fragment.libraries,
+ initializeEmptyHolders: true);
js.Statement mainCode = js.js.statement(_mainBoilerplate, {
// TODO(29455): 'hunkHelpers' displaces other names, so don't minify it.
@@ -791,10 +820,10 @@
return new js.Block(holderInits);
}
- js.Expression emitDeferredFragment(DeferredFragment fragment,
- js.Expression deferredTypes, List<Holder> holders) {
+ js.Expression emitDeferredFragment(
+ FinalizedFragment fragment, List<Holder> holders) {
HolderCode holderCode =
- emitHolders(holders, fragment, initializeEmptyHolders: false);
+ emitHolders(holders, fragment.libraries, initializeEmptyHolders: false);
List<Holder> nonStaticStateHolders = holders
.where((Holder holder) => !holder.isStaticStateHolder)
@@ -821,19 +850,6 @@
}
}
- var classPrototypes = emitPrototypes(fragment, includeClosures: false);
- var closurePrototypes = emitPrototypes(fragment, includeClosures: true);
- var inheritance = emitInheritance(fragment);
- var methodAliases = emitInstanceMethodAliases(fragment);
- var tearOffs = emitInstallTearOffs(fragment);
- var constants = emitConstants(fragment);
- var typeRules = emitTypeRules(fragment);
- var variances = emitVariances(fragment);
- var staticNonFinalFields = emitStaticNonFinalFields(fragment);
- var lazyInitializers = emitLazilyInitializedStatics(fragment);
- // TODO(floitsch): only call emitNativeSupport if we need native.
- var nativeSupport = emitNativeSupport(fragment);
-
// TODO(sra): How do we tell if [deferredTypes] is empty? It is filled-in
// later via the program finalizers. So we should defer the decision on the
// emptiness of the fragment until the finalizers have run. For now we seem
@@ -843,16 +859,7 @@
// not emit any functions, then we probably did not use the signature types
// in the OutputUnit's types, leaving them unused and tree-shaken.
- if (holderCode.activeHolders.isEmpty &&
- isEmptyStatement(classPrototypes) &&
- isEmptyStatement(closurePrototypes) &&
- isEmptyStatement(inheritance) &&
- isEmptyStatement(methodAliases) &&
- isEmptyStatement(tearOffs) &&
- isEmptyStatement(constants) &&
- isEmptyStatement(staticNonFinalFields) &&
- isEmptyStatement(lazyInitializers) &&
- isEmptyStatement(nativeSupport)) {
+ if (holderCode.activeHolders.isEmpty && fragment.isEmpty) {
return null;
}
@@ -865,18 +872,18 @@
.map((holder) => js.js("#", holder.name))
.toList(growable: false)),
'updateHolders': new js.Block(updateHolderAssignments),
- 'prototypes': classPrototypes,
- 'closures': closurePrototypes,
- 'inheritance': inheritance,
- 'aliases': methodAliases,
- 'tearOffs': tearOffs,
- 'typeRules': typeRules,
- 'variances': variances,
- 'constants': constants,
- 'staticNonFinalFields': staticNonFinalFields,
- 'lazyStatics': lazyInitializers,
- 'types': deferredTypes,
- 'nativeSupport': nativeSupport,
+ 'prototypes': fragment.classPrototypes,
+ 'closures': fragment.closurePrototypes,
+ 'inheritance': fragment.inheritance,
+ 'aliases': fragment.methodAliases,
+ 'tearOffs': fragment.tearOffs,
+ 'typeRules': fragment.typeRules,
+ 'variances': fragment.variances,
+ 'constants': fragment.constants,
+ 'staticNonFinalFields': fragment.staticNonFinalFields,
+ 'lazyStatics': fragment.lazyInitializers,
+ 'types': fragment.deferredTypes,
+ 'nativeSupport': fragment.nativeSupport,
'typesOffset': _namer.typesOffsetName,
'sharedStrings': StringReferenceResource(),
'sharedTypeRtis': TypeReferenceResource(),
@@ -904,7 +911,7 @@
///
/// The emitted holders contain classes (only the constructors) and all
/// static functions.
- HolderCode emitHolders(List<Holder> holders, Fragment fragment,
+ HolderCode emitHolders(List<Holder> holders, List<Library> libraries,
{bool initializeEmptyHolders}) {
assert(initializeEmptyHolders != null);
// Skip the static-state holder in this function.
@@ -918,7 +925,7 @@
holderCode[holder] = <js.Property>[];
}
- for (Library library in fragment.libraries) {
+ for (Library library in libraries) {
for (StaticMethod method in library.statics) {
assert(!method.holder.isStaticStateHolder);
Map<js.Name, js.Expression> propertyMap = emitStaticMethod(method);
@@ -1488,13 +1495,6 @@
return js.js.statement('(function #(){#})();', [name, block]);
}
- bool isEmptyStatement(js.Statement statement) {
- if (statement is js.Block) {
- return statement.statements.isEmpty;
- }
- return statement is js.EmptyStatement;
- }
-
/// Emits the section that installs tear-off getters.
js.Statement emitInstallTearOffs(Fragment fragment,
{bool softDeferred = false}) {
@@ -1864,20 +1864,20 @@
// array of hashes indexed by part.
// [deferredLoadHashes] may have missing entries to indicate empty parts.
void finalizeDeferredLoadingData(
- Map<String, List<Fragment>> loadMap,
- Map<DeferredFragment, String> deferredLoadHashes,
+ Map<String, List<FinalizedFragment>> loadMap,
+ Map<FinalizedFragment, String> deferredLoadHashes,
DeferredLoadingState deferredLoadingState) {
if (loadMap.isEmpty) return;
- Map<Fragment, int> fragmentIndexes = {};
+ Map<FinalizedFragment, int> fragmentIndexes = {};
List<String> fragmentUris = [];
List<String> fragmentHashes = [];
List<js.Property> libraryPartsMapEntries = [];
- loadMap.forEach((String loadId, List<Fragment> fragmentList) {
+ loadMap.forEach((String loadId, List<FinalizedFragment> fragmentList) {
List<js.Expression> indexes = [];
- for (Fragment fragment in fragmentList) {
+ for (FinalizedFragment fragment in fragmentList) {
String fragmentHash = deferredLoadHashes[fragment];
if (fragmentHash == null) continue;
int index = fragmentIndexes[fragment];
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
new file mode 100644
index 0000000..576f4ee
--- /dev/null
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
@@ -0,0 +1,372 @@
+// Copyright (c) 2020, 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 '../../deferred_load.dart' show OutputUnit;
+import '../../js/js.dart' as js;
+import '../../js/size_estimator.dart';
+import '../../options.dart';
+import '../model.dart';
+
+class PreFragment {
+ final List<DeferredFragment> fragments = [];
+ final List<js.Statement> classPrototypes = [];
+ final List<js.Statement> closurePrototypes = [];
+ final List<js.Statement> inheritance = [];
+ final List<js.Statement> methodAliases = [];
+ final List<js.Statement> tearOffs = [];
+ final List<js.Statement> constants = [];
+ final List<js.Statement> typeRules = [];
+ final List<js.Statement> variances = [];
+ final List<js.Statement> staticNonFinalFields = [];
+ final List<js.Statement> lazyInitializers = [];
+ final List<js.Statement> nativeSupport = [];
+ final Set<PreFragment> successors = {};
+ final Set<PreFragment> predecessors = {};
+ int size = 0;
+
+ PreFragment(
+ Fragment fragment,
+ js.Statement classPrototypes,
+ js.Statement closurePrototypes,
+ js.Statement inheritance,
+ js.Statement methodAliases,
+ js.Statement tearOffs,
+ js.Statement constants,
+ js.Statement typeRules,
+ js.Statement variances,
+ js.Statement staticNonFinalFields,
+ js.Statement lazyInitializers,
+ js.Statement nativeSupport,
+ bool estimateSize) {
+ this.fragments.add(fragment);
+ this.classPrototypes.add(classPrototypes);
+ this.closurePrototypes.add(closurePrototypes);
+ this.inheritance.add(inheritance);
+ this.methodAliases.add(methodAliases);
+ this.tearOffs.add(tearOffs);
+ this.constants.add(constants);
+ this.typeRules.add(typeRules);
+ this.variances.add(variances);
+ this.staticNonFinalFields.add(staticNonFinalFields);
+ this.lazyInitializers.add(lazyInitializers);
+ this.nativeSupport.add(nativeSupport);
+ if (estimateSize) {
+ var estimator = SizeEstimator();
+ estimator.visit(classPrototypes);
+ estimator.visit(closurePrototypes);
+ estimator.visit(inheritance);
+ estimator.visit(methodAliases);
+ estimator.visit(tearOffs);
+ estimator.visit(constants);
+ estimator.visit(typeRules);
+ estimator.visit(variances);
+ estimator.visit(staticNonFinalFields);
+ estimator.visit(lazyInitializers);
+ estimator.visit(nativeSupport);
+ size = estimator.charCount;
+ }
+ }
+
+ void mergeAfter(PreFragment that) {
+ assert(this != that);
+ assert(
+ (that.predecessors.length == 1 && that.predecessors.single == this) ||
+ (this.successors.length == 1 && this.successors.single == that));
+ this.fragments.addAll(that.fragments);
+ this.classPrototypes.addAll(that.classPrototypes);
+ this.closurePrototypes.addAll(that.closurePrototypes);
+ this.inheritance.addAll(that.inheritance);
+ this.methodAliases.addAll(that.methodAliases);
+ this.tearOffs.addAll(that.tearOffs);
+ this.constants.addAll(that.constants);
+ this.typeRules.addAll(that.typeRules);
+ this.variances.addAll(that.variances);
+ this.staticNonFinalFields.addAll(that.staticNonFinalFields);
+ this.lazyInitializers.addAll(that.lazyInitializers);
+ this.nativeSupport.addAll(that.nativeSupport);
+ this.successors.remove(that);
+ that.successors.forEach((fragment) {
+ fragment.predecessors.remove(that);
+ fragment.predecessors.add(this);
+ });
+ this.successors.addAll(that.successors);
+ that.predecessors.remove(this);
+ that.predecessors.forEach((fragment) {
+ fragment.successors.remove(that);
+ fragment.successors.add(this);
+ });
+ this.predecessors.addAll(that.predecessors);
+ this.size += that.size;
+ }
+
+ FinalizedFragment finalize(
+ Program program, Map<Fragment, FinalizedFragment> fragmentMap) {
+ FinalizedFragment finalizedFragment;
+ var seedFragment = fragments.first;
+
+ // If we only have a single fragment, then wen just finalize it by itself.
+ // Otherwise, we finalize an entire group of fragments into a single
+ // merged and finalized fragment.
+ if (fragments.length == 1) {
+ finalizedFragment = FinalizedFragment(
+ seedFragment.outputFileName,
+ seedFragment.outputUnit,
+ seedFragment.libraries,
+ classPrototypes.first,
+ closurePrototypes.first,
+ inheritance.first,
+ methodAliases.first,
+ tearOffs.first,
+ constants.first,
+ typeRules.first,
+ variances.first,
+ staticNonFinalFields.first,
+ lazyInitializers.first,
+ nativeSupport.first,
+ program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+ fragmentMap[seedFragment] = finalizedFragment;
+ } else {
+ List<Library> libraries = [];
+ for (var fragment in fragments) {
+ if (seedFragment.outputUnit != fragment.outputUnit) {
+ program.mergeOutputUnitMetadata(
+ seedFragment.outputUnit, fragment.outputUnit);
+ seedFragment.outputUnit.merge(fragment.outputUnit);
+ }
+ libraries.addAll(fragment.libraries);
+ }
+ finalizedFragment = FinalizedFragment(
+ seedFragment.outputFileName,
+ seedFragment.outputUnit,
+ libraries,
+ js.Block(classPrototypes),
+ js.Block(closurePrototypes),
+ js.Block(inheritance),
+ js.Block(methodAliases),
+ js.Block(tearOffs),
+ js.Block(constants),
+ js.Block(typeRules),
+ js.Block(variances),
+ js.Block(staticNonFinalFields),
+ js.Block(lazyInitializers),
+ js.Block(nativeSupport),
+ program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+ for (var fragment in fragments) {
+ fragmentMap[fragment] = finalizedFragment;
+ }
+ }
+ return finalizedFragment;
+ }
+
+ @override
+ String toString() {
+ // This is not an efficient operation and should only be used for debugging.
+ var successors =
+ this.successors.map((fragment) => fragment.debugName()).join(',');
+ var predecessors =
+ this.predecessors.map((fragment) => fragment.debugName()).join(',');
+ var name = debugName();
+ return 'PreFragment(fragments=[$name], successors=[$successors], '
+ 'predecessors=[$predecessors])';
+ }
+
+ String debugName() {
+ List<String> names = [];
+ this.fragments.forEach((fragment) => names.add(fragment.name));
+ return names.join(',');
+ }
+
+ static int compare(PreFragment l, PreFragment r) {
+ return l.size.compareTo(r.size);
+ }
+}
+
+class FinalizedFragment {
+ final String outputFileName;
+ final OutputUnit outputUnit;
+ final List<Library> libraries;
+ final js.Statement classPrototypes;
+ final js.Statement closurePrototypes;
+ final js.Statement inheritance;
+ final js.Statement methodAliases;
+ final js.Statement tearOffs;
+ final js.Statement constants;
+ final js.Statement typeRules;
+ final js.Statement variances;
+ final js.Statement staticNonFinalFields;
+ final js.Statement lazyInitializers;
+ final js.Statement nativeSupport;
+ final js.Expression deferredTypes;
+
+ FinalizedFragment(
+ this.outputFileName,
+ this.outputUnit,
+ this.libraries,
+ this.classPrototypes,
+ this.closurePrototypes,
+ this.inheritance,
+ this.methodAliases,
+ this.tearOffs,
+ this.constants,
+ this.typeRules,
+ this.variances,
+ this.staticNonFinalFields,
+ this.lazyInitializers,
+ this.nativeSupport,
+ this.deferredTypes);
+
+ bool isEmptyStatement(js.Statement statement) {
+ if (statement is js.Block) {
+ return statement.statements.isEmpty;
+ }
+ return statement is js.EmptyStatement;
+ }
+
+ bool get isEmpty {
+ // TODO(sra): How do we tell if [deferredTypes] is empty? It is filled-in
+ // later via the program finalizers. So we should defer the decision on the
+ // emptiness of the fragment until the finalizers have run. For now we seem
+ // to get away with the fact that type indexes are either (1) main unit or
+ // (2) local to the emitted unit, so there is no such thing as a type in a
+ // deferred unit that is referenced from another deferred unit. If we did
+ // not emit any functions, then we probably did not use the signature types
+ // in the OutputUnit's types, leaving them unused and tree-shaken.
+ // TODO(joshualitt): Currently, we ignore [typeRules] when determining
+ // emptiness because the type rules never seem to be empty.
+ return isEmptyStatement(classPrototypes) &&
+ isEmptyStatement(closurePrototypes) &&
+ isEmptyStatement(inheritance) &&
+ isEmptyStatement(methodAliases) &&
+ isEmptyStatement(tearOffs) &&
+ isEmptyStatement(constants) &&
+ isEmptyStatement(staticNonFinalFields) &&
+ isEmptyStatement(lazyInitializers) &&
+ isEmptyStatement(nativeSupport);
+ }
+}
+
+class FragmentMerger {
+ final CompilerOptions _options;
+
+ FragmentMerger(this._options);
+
+ // Converts a map of (loadId, List<fragments>) to a map of
+ // (loadId, List<FinalizedFragment>).
+ static Map<String, List<FinalizedFragment>> processLoadMap(
+ Map<String, List<Fragment>> programLoadMap,
+ Map<Fragment, FinalizedFragment> fragmentMap) {
+ Map<String, List<FinalizedFragment>> loadMap = {};
+ programLoadMap.forEach((loadId, fragments) {
+ Set<FinalizedFragment> unique = {};
+ List<FinalizedFragment> finalizedFragments = [];
+ loadMap[loadId] = finalizedFragments;
+ for (var fragment in fragments) {
+ var finalizedFragment = fragmentMap[fragment];
+ if (unique.add(finalizedFragment)) {
+ finalizedFragments.add(finalizedFragment);
+ }
+ }
+ });
+ return loadMap;
+ }
+
+ // Attaches predecessors to each PreFragment. We only care about
+ // direct predecessors.
+ static void attachDependencies(Map<String, List<Fragment>> programLoadMap,
+ Map<Fragment, PreFragment> fragmentMap) {
+ programLoadMap.forEach((loadId, fragments) {
+ for (int i = 0; i < fragments.length - 1; i++) {
+ var fragment = fragmentMap[fragments[i]];
+ var nextFragment = fragmentMap[fragments[i + 1]];
+ fragment.successors.add(nextFragment);
+ nextFragment.predecessors.add(fragment);
+ }
+ });
+ }
+
+ // Iterates through preDeferredFragments making as many merges as possible
+ // until either there are no more valid merges to make, or until there are
+ // only mergeFragmentsThreshold remaining.
+ List<PreFragment> mergeFragments(List<PreFragment> preDeferredFragments) {
+ Set<PreFragment> fragmentsBySize = {};
+
+ // We greedily look for a valid merge which results in the smallest
+ // possible increase in size. Currently, we only merge fragments in two
+ // cases:
+ // 1) We will merge two fragments A and B if B is A's single dependent.
+ // 2) We will merge two fragments C and D if C is D's single dependency.
+ bool mergeTwo() {
+ PreFragment aFragment = null;
+ PreFragment bFragment = null;
+ PreFragment cFragment = null;
+ PreFragment dFragment = null;
+ for (var fragment in fragmentsBySize) {
+ if (fragment.successors.length == 1 &&
+ (aFragment == null && bFragment == null ||
+ (fragment.size + fragment.successors.single.size <
+ aFragment.size + bFragment.size))) {
+ aFragment = fragment;
+ bFragment = fragment.successors.single;
+ }
+ if (fragment.predecessors.length == 1 &&
+ (cFragment == null && dFragment == null ||
+ (fragment.size + fragment.predecessors.single.size <
+ cFragment.size + dFragment.size))) {
+ cFragment = fragment.predecessors.single;
+ dFragment = fragment;
+ }
+ }
+ assert((aFragment != null &&
+ bFragment != null &&
+ aFragment != bFragment &&
+ aFragment.successors.single == bFragment) ||
+ (cFragment != null &&
+ dFragment != null &&
+ cFragment != dFragment &&
+ dFragment.predecessors.single == cFragment) ||
+ (aFragment == null &&
+ bFragment == null &&
+ cFragment == null &&
+ dFragment == null));
+ int mergeSentinel = 0x10000000000;
+ bool abCanMerge = aFragment != null && bFragment != null;
+ bool cdCanMerge = cFragment != null && dFragment != null;
+ int abMergeSize =
+ abCanMerge ? aFragment.size + bFragment.size : mergeSentinel;
+ int cdMergeSize =
+ cdCanMerge ? cFragment.size + dFragment.size : mergeSentinel;
+ bool abShouldMerge() => abCanMerge && abMergeSize <= cdMergeSize;
+ bool cdShouldMerge() => cdCanMerge && cdMergeSize <= abMergeSize;
+ void innerMerge(PreFragment a, PreFragment b) {
+ fragmentsBySize.remove(a);
+ fragmentsBySize.remove(b);
+ a.mergeAfter(b);
+ fragmentsBySize.add(a);
+ }
+
+ bool merged = abShouldMerge() || cdShouldMerge();
+ if (abShouldMerge()) {
+ innerMerge(aFragment, bFragment);
+ } else if (cdShouldMerge()) {
+ innerMerge(cFragment, dFragment);
+ } else {
+ assert(aFragment == null &&
+ bFragment == null &&
+ cFragment == null &&
+ dFragment == null);
+ }
+ return merged;
+ }
+
+ fragmentsBySize.addAll(preDeferredFragments);
+ var numFragments = preDeferredFragments.length;
+ while (numFragments-- > _options.mergeFragmentsThreshold) {
+ if (!mergeTwo()) {
+ // No further valid merges can be made.
+ break;
+ }
+ }
+ return fragmentsBySize.toList();
+ }
+}
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index f549f60..60d8baa 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -38,6 +38,7 @@
import '../../constants/values.dart'
show ConstantValue, FunctionConstantValue, NullConstantValue;
import '../../common_elements.dart' show CommonElements, JElementEnvironment;
+import '../../deferred_load.dart' show OutputUnit;
import '../../dump_info.dart';
import '../../elements/entities.dart';
import '../../elements/types.dart';
@@ -75,6 +76,7 @@
import '../js_emitter.dart' show buildTearOffCode, NativeGenerator;
import '../model.dart';
import '../native_emitter.dart';
+import 'fragment_merger.dart';
part 'fragment_emitter.dart';
@@ -94,9 +96,11 @@
final SourceInformationStrategy _sourceInformationStrategy;
// The full code that is written to each hunk part-file.
- final Map<Fragment, CodeOutput> outputBuffers = {};
+ final Map<OutputUnit, CodeOutput> emittedOutputBuffers = {};
- Set<Fragment> omittedFragments = Set();
+ final Set<OutputUnit> omittedOutputUnits = {};
+
+ List<PreFragment> preDeferredFragmentsForTesting;
/// For deferred loading we communicate the initializers via this global var.
static const String deferredInitializersGlobal =
@@ -183,6 +187,8 @@
[_namer.globalObjectForConstant(value), _namer.constantName(value)]);
}
+ bool get shouldMergeFragments => _options.mergeFragmentsThreshold != null;
+
int emitProgram(Program program, CodegenWorld codegenWorld) {
MainFragment mainFragment = program.fragments.first;
List<DeferredFragment> deferredFragments =
@@ -203,17 +209,44 @@
js.Statement mainCode =
fragmentEmitter.emitMainFragment(program, deferredLoadingState);
- Map<DeferredFragment, js.Expression> deferredFragmentsCode = {};
+ // In order to get size estimates, we partially emit deferred fragments.
+ List<PreFragment> preDeferredFragments = [];
+ Map<DeferredFragment, PreFragment> preFragmentMap = {};
+ _task.measureSubtask('emit prefragments', () {
+ for (var fragment in deferredFragments) {
+ var preFragment =
+ fragmentEmitter.emitPreFragment(fragment, shouldMergeFragments);
+ preFragmentMap[fragment] = preFragment;
+ preDeferredFragments.add(preFragment);
+ }
+ });
- for (DeferredFragment fragment in deferredFragments) {
- js.Expression types =
- program.metadataTypesForOutputUnit(fragment.outputUnit);
+ // Attach dependencies to each PreFragment.
+ FragmentMerger.attachDependencies(program.loadMap, preFragmentMap);
+
+ if (shouldMergeFragments) {
+ preDeferredFragments = _task.measureSubtask('merge fragments', () {
+ FragmentMerger fragmentMerger = FragmentMerger(_options);
+ return fragmentMerger.mergeFragments(preDeferredFragments);
+ });
+ }
+
+ // If necessary, we retain the merged PreFragments for testing.
+ if (retainDataForTesting) {
+ preDeferredFragmentsForTesting = preDeferredFragments;
+ }
+
+ Map<DeferredFragment, FinalizedFragment> fragmentMap = {};
+ Map<FinalizedFragment, js.Expression> deferredFragmentsCode = {};
+ for (var preDeferredFragment in preDeferredFragments) {
+ var finalizedFragment =
+ preDeferredFragment.finalize(program, fragmentMap);
js.Expression fragmentCode = fragmentEmitter.emitDeferredFragment(
- fragment, types, program.holders);
+ finalizedFragment, program.holders);
if (fragmentCode != null) {
- deferredFragmentsCode[fragment] = fragmentCode;
+ deferredFragmentsCode[finalizedFragment] = fragmentCode;
} else {
- omittedFragments.add(fragment);
+ omittedOutputUnits.add(finalizedFragment.outputUnit);
}
}
@@ -227,15 +260,17 @@
// deferred ASTs inside the parts) have any contents. We should wait until
// this point to decide if a part is empty.
- Map<DeferredFragment, String> hunkHashes =
+ Map<FinalizedFragment, String> hunkHashes =
_task.measureSubtask('write fragments', () {
return writeDeferredFragments(deferredFragmentsCode);
});
// Now that we have written the deferred hunks, we can create the deferred
// loading data.
+ Map<String, List<FinalizedFragment>> loadMap =
+ FragmentMerger.processLoadMap(program.loadMap, fragmentMap);
fragmentEmitter.finalizeDeferredLoadingData(
- program.loadMap, hunkHashes, deferredLoadingState);
+ loadMap, hunkHashes, deferredLoadingState);
_task.measureSubtask('write fragments', () {
writeMainFragment(mainFragment, mainCode,
@@ -254,7 +289,7 @@
}
// Return the total program size.
- return outputBuffers.values.fold(0, (a, b) => a + b.length);
+ return emittedOutputBuffers.values.fold(0, (a, b) => a + b.length);
}
/// Generates a simple header that provides the compiler's build id.
@@ -278,11 +313,11 @@
/// library code).
///
/// Updates the shared [outputBuffers] field with the output.
- Map<DeferredFragment, String> writeDeferredFragments(
- Map<DeferredFragment, js.Expression> fragmentsCode) {
- Map<DeferredFragment, String> hunkHashes = {};
+ Map<FinalizedFragment, String> writeDeferredFragments(
+ Map<FinalizedFragment, js.Expression> fragmentsCode) {
+ Map<FinalizedFragment, String> hunkHashes = {};
- fragmentsCode.forEach((DeferredFragment fragment, js.Expression code) {
+ fragmentsCode.forEach((FinalizedFragment fragment, js.Expression code) {
hunkHashes[fragment] = writeDeferredFragment(fragment, code);
});
@@ -313,7 +348,7 @@
CodeOutput mainOutput = StreamCodeOutput(
_outputProvider.createOutputSink('', 'js', OutputType.js),
codeOutputListeners);
- outputBuffers[fragment] = mainOutput;
+ emittedOutputBuffers[fragment.outputUnit] = mainOutput;
js.Program program = js.Program([
buildGeneratedBy(),
@@ -358,7 +393,7 @@
// Returns the deferred fragment's hash.
//
// Updates the shared [outputBuffers] field with the output.
- String writeDeferredFragment(DeferredFragment fragment, js.Expression code) {
+ String writeDeferredFragment(FinalizedFragment fragment, js.Expression code) {
List<CodeOutputListener> outputListeners = [];
Hasher hasher = new Hasher();
outputListeners.add(hasher);
@@ -378,7 +413,7 @@
hunkPrefix, deferredExtension, OutputType.jsPart),
outputListeners);
- outputBuffers[fragment] = output;
+ emittedOutputBuffers[fragment.outputUnit] = output;
// The [code] contains the function that must be invoked when the deferred
// hunk is loaded.
@@ -456,8 +491,7 @@
"needed for a given deferred library import.";
mapping.addAll(_closedWorld.outputUnitData.computeDeferredMap(
_options, _closedWorld.elementEnvironment,
- omittedUnits:
- omittedFragments.map((fragment) => fragment.outputUnit).toSet()));
+ omittedUnits: omittedOutputUnits));
_outputProvider.createOutputSink(
_options.deferredMapUri.path, '', OutputType.deferredMap)
..add(const JsonEncoder.withIndent(" ").convert(mapping))
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 47b0985..6e86321 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -161,6 +161,15 @@
/// libraries are subdivided.
Uri deferredMapUri;
+ /// The maximum number of deferred fragments to generate. If the number of
+ /// fragments exceeds this amount, then they may be merged.
+ /// Note: Currently, we only merge fragments in a single dependency chain. We
+ /// will not merge fragments with unrelated dependencies and thus we may
+ /// generate more fragments than the 'mergeFragmentsThreshold' under some
+ /// situations.
+ int mergeFragmentsThreshold = null; // default value, no max.
+ int _mergeFragmentsThreshold;
+
/// Whether to disable inlining during the backend optimizations.
// TODO(sigmund): negate, so all flags are positive
bool disableInlining = false;
@@ -524,7 +533,9 @@
..cfeOnly = _hasOption(options, Flags.cfeOnly)
..debugGlobalInference = _hasOption(options, Flags.debugGlobalInference)
.._soundNullSafety = _hasOption(options, Flags.soundNullSafety)
- .._noSoundNullSafety = _hasOption(options, Flags.noSoundNullSafety);
+ .._noSoundNullSafety = _hasOption(options, Flags.noSoundNullSafety)
+ .._mergeFragmentsThreshold =
+ _extractIntOption(options, '${Flags.mergeFragmentsThreshold}=');
}
void validate() {
@@ -628,6 +639,10 @@
if (_noNativeNullAssertions || nullSafetyMode != NullSafetyMode.sound) {
nativeNullAssertions = false;
}
+
+ if (_mergeFragmentsThreshold != null) {
+ mergeFragmentsThreshold = _mergeFragmentsThreshold;
+ }
}
/// Returns `true` if warnings and hints are shown for all packages.
diff --git a/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart b/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart
index ca84a9f..ab1d73d 100644
--- a/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart
index bb45d8c..af43fe3 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart
index 5a1c77d..fa722a4 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib2}], usedBy: [], needs: []}],
+ steps=[lib2=(f1)]
+*/
+
// @dart = 2.7
import 'lib1.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart
index 71a715b..b4cc534 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'package:expect/expect.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart
index 5bc723e..0810049 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart
@@ -2,6 +2,15 @@
// 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:
+ output_units=[
+ f1: {units: [1{l1}], usedBy: [], needs: []},
+ f2: {units: [2{l2}], usedBy: [], needs: []}],
+ steps=[
+ l1=(f1),
+ l2=(f2)]
+*/
+
// @dart = 2.7
import 'shared.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart
index 167b145..ba1eed1 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
// Test that when a deferred import fails to load, it is possible to retry.
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart
index 3c99316..48dd14a 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
// Test that loading of a library (with top-level functions only) can
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
index 782e177..1da36d1 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [1{lib1, lib2}], usedBy: [2, 3], needs: []},
+ f2: {units: [2{lib1}], usedBy: [], needs: [1]},
+ f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [1{lib1, lib2}, 2{lib1}], usedBy: [2], needs: []},
+ f2: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1),
+ lib2=(f1, f2)]
+*/
+
// @dart = 2.7
import 'lib1.dart' deferred as lib1;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart
index 20bb44c..45bddff 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'lib1.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart
index 9c56b84..c838c8f 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib1}], usedBy: [], needs: []}],
+ steps=[lib1=(f1)]
+*/
+
// @dart = 2.7
import 'lib1.dart' deferred as lib1;
diff --git a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
index e55be81..d65299a 100644
--- a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: [1]},
+ f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [2{lib1, lib2}, 3{lib2}], usedBy: [2], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1)]
+*/
+
// @dart = 2.7
// TODO(sigmund): remove this indirection and move the main code here. This is
diff --git a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart
index 3a45b3e..59d567e 100644
--- a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart b/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart
index 74cd88d..0465c9f 100644
--- a/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import "lib.dart" deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/future_or/main.dart b/pkg/compiler/test/deferred_loading/data/future_or/main.dart
index de44d54..77ca4a0 100644
--- a/pkg/compiler/test/deferred_loading/data/future_or/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/future_or/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib1}], usedBy: [], needs: []}],
+ steps=[lib1=(f1)]
+*/
+
// @dart = 2.7
import 'dart:async';
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart
index 5da17d9..9b83f62 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{b}], usedBy: [], needs: []}],
+ steps=[b=(f1)]
+*/
+
// @dart = 2.7
// Test instantiation used only in a deferred library.
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
index ae2c5a7..963d421 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
+ f2: {units: [1{b}], usedBy: [], needs: [1]},
+ f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [2{b, c}, 1{b}], usedBy: [2], needs: []},
+ f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1),
+ c=(f1, f2)]
+*/
+
// @dart = 2.7
// Test instantiations with different type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
index 3ff8f46..53cbbdb 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
+ f2: {units: [2{b}], usedBy: [], needs: [1]},
+ f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [1{b, c}, 2{b}], usedBy: [2], needs: []},
+ f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1),
+ c=(f1, f2)]
+*/
+
// @dart = 2.7
// Test instantiations with the same type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart
index b0d3a8f..10e0815 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{b}], usedBy: [], needs: []}],
+ steps=[b=(f1)]
+*/
+
// @dart = 2.7
// Test instantiation used only in a deferred library.
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
index 34bf22a..c6b601b 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
+ f2: {units: [1{b}], usedBy: [], needs: [1]},
+ f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [2{b, c}, 1{b}], usedBy: [2], needs: []},
+ f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1),
+ c=(f1, f2)]
+*/
+
// @dart = 2.7
// Test instantiations with different type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
index 5612525..8dbfaf1 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
+ f2: {units: [2{b}], usedBy: [], needs: [1]},
+ f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [1{b, c}, 2{b}], usedBy: [2], needs: []},
+ f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+ b=(f1),
+ c=(f1, f2)]
+*/
+
// @dart = 2.7
// Test instantiations with the same type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart b/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart
index d4b10fb..491bcf6 100644
--- a/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
index ca9bb68..441c28d 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
@@ -2,6 +2,41 @@
// 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.
+/*spec.library:
+ output_units=[
+ f1: {units: [3{libA, libB, libC}], usedBy: [2, 4], needs: []},
+ f2: {units: [4{libA, libC}], usedBy: [3, 6], needs: [1, 4]},
+ f3: {units: [6{libA}], usedBy: [], needs: [2]},
+ f4: {units: [5{libB, libC}], usedBy: [5, 2], needs: [1]},
+ f5: {units: [1{libB}], usedBy: [], needs: [4]},
+ f6: {units: [2{libC}], usedBy: [], needs: [2]}],
+ steps=[
+ libA=(f1, f2, f3),
+ libB=(f1, f4, f5),
+ libC=(f1, f4, f2, f6)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}, 6{libA}, 1{libB}], usedBy: [2], needs: []},
+ f2: {units: [2{libC}], usedBy: [], needs: [1]}],
+ steps=[
+ libA=(f1),
+ libB=(f1),
+ libC=(f1, f2)]
+*/
+
+/*three-frag.library:
+ output_units=[
+ f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}, 6{libA}], usedBy: [2, 3], needs: []},
+ f2: {units: [1{libB}], usedBy: [], needs: [1]},
+ f3: {units: [2{libC}], usedBy: [], needs: [1]}],
+ steps=[
+ libA=(f1),
+ libB=(f1, f2),
+ libC=(f1, f3)]
+*/
+
// @dart = 2.7
import 'liba.dart' deferred as libA;
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
index 7f86fc1..85002e0 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
@@ -35,7 +35,8 @@
f_010_11(Set<String> u, int b) => v(u, '01011', b);
@pragma('dart2js:noInline')
-/*member: f_011_01:member_unit=8{b1, b3, b4}*/
+/*spec|two-frag.member: f_011_01:member_unit=8{b1, b3, b4}*/
+/*three-frag.member: f_011_01:member_unit=8{b1, b3, b4, b2, b5}*/
f_011_01(Set<String> u, int b) => v(u, '01101', b);
@pragma('dart2js:noInline')
@@ -51,7 +52,8 @@
f_100_11(Set<String> u, int b) => v(u, '10011', b);
@pragma('dart2js:noInline')
-/*member: f_101_01:member_unit=12{b1, b3, b5}*/
+/*spec|three-frag.member: f_101_01:member_unit=12{b1, b3, b5}*/
+/*two-frag.member: f_101_01:member_unit=12{b1, b3, b5, b4, b2}*/
f_101_01(Set<String> u, int b) => v(u, '10101', b);
@pragma('dart2js:noInline')
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
index 7c057b4..0fc9253 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
@@ -2,6 +2,72 @@
// 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.
+/*spec.library:
+ output_units=[
+ f10: {units: [7{b1, b2, b4}], usedBy: [11, 29], needs: [9, 8]},
+ f11: {units: [5{b1, b2, b3}], usedBy: [12, 21, 26], needs: [10, 8]},
+ f12: {units: [10{b1, b5}], usedBy: [13, 31], needs: [11, 21]},
+ f13: {units: [6{b1, b4}], usedBy: [14, 30], needs: [12, 22]},
+ f14: {units: [4{b1, b3}], usedBy: [15, 28], needs: [13, 23]},
+ f15: {units: [3{b1, b2}], usedBy: [16, 24], needs: [14, 23]},
+ f16: {units: [2{b1}], usedBy: [], needs: [15]},
+ f17: {units: [24{b2, b3, b4, b5}], usedBy: [3, 2], needs: [1]},
+ f18: {units: [23{b2, b4, b5}], usedBy: [19, 20], needs: [5, 25]},
+ f19: {units: [22{b2, b3, b5}], usedBy: [20, 6], needs: [18, 25]},
+ f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2, 17], needs: []},
+ f20: {units: [20{b2, b3, b4}], usedBy: [9, 7, 6], needs: [19, 18]},
+ f21: {units: [21{b2, b5}], usedBy: [22, 12], needs: [11, 26]},
+ f22: {units: [19{b2, b4}], usedBy: [23, 13], needs: [21, 27]},
+ f23: {units: [18{b2, b3}], usedBy: [15, 14], needs: [22, 27]},
+ f24: {units: [17{b2}], usedBy: [], needs: [15]},
+ f25: {units: [28{b3, b4, b5}], usedBy: [19, 18], needs: [5, 4]},
+ f26: {units: [27{b3, b5}], usedBy: [27, 21], needs: [11, 29]},
+ f27: {units: [26{b3, b4}], usedBy: [23, 22], needs: [26, 29]},
+ f28: {units: [25{b3}], usedBy: [], needs: [14]},
+ f29: {units: [30{b4, b5}], usedBy: [27, 26], needs: [10, 9]},
+ f2: {units: [16{b1, b3, b4, b5}], usedBy: [3, 4], needs: [1, 17]},
+ f30: {units: [29{b4}], usedBy: [], needs: [13]},
+ f31: {units: [31{b5}], usedBy: [], needs: [12]},
+ f3: {units: [15{b1, b2, b4, b5}], usedBy: [4, 5], needs: [2, 17]},
+ f4: {units: [13{b1, b2, b3, b5}], usedBy: [5, 25], needs: [3, 2]},
+ f5: {units: [9{b1, b2, b3, b4}], usedBy: [6, 18, 25], needs: [4, 3]},
+ f6: {units: [14{b1, b4, b5}], usedBy: [7, 8], needs: [5, 20, 19]},
+ f7: {units: [12{b1, b3, b5}], usedBy: [8, 9], needs: [6, 20]},
+ f8: {units: [8{b1, b3, b4}], usedBy: [9, 11, 10], needs: [7, 6]},
+ f9: {units: [11{b1, b2, b5}], usedBy: [10, 29], needs: [8, 20, 7]}],
+ steps=[
+ b1=(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16),
+ b2=(f1, f17, f3, f4, f5, f18, f19, f20, f9, f10, f11, f21, f22, f23, f15, f24),
+ b3=(f1, f17, f2, f4, f5, f25, f19, f20, f7, f8, f11, f26, f27, f23, f14, f28),
+ b4=(f1, f17, f2, f3, f5, f25, f18, f20, f6, f8, f10, f29, f27, f22, f13, f30),
+ b5=(f1, f17, f2, f3, f4, f25, f18, f19, f6, f7, f9, f29, f26, f21, f12, f31)]
+*/
+
+/*three-frag.library:
+ output_units=[
+ f1: {units: [1{b1, b2, b3, b4, b5}, 24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}], usedBy: [2, 3], needs: []},
+ f2: {units: [12{b1, b3, b5}], usedBy: [3], needs: [1]},
+ f3: {units: [8{b1, b3, b4, b2, b5}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 31{b5}, 6{b1, b4}, 29{b4}, 4{b1, b3}, 25{b3}, 3{b1, b2}, 2{b1}, 17{b2}], usedBy: [], needs: [2, 1]}],
+ steps=[
+ b1=(f1, f2, f3),
+ b2=(f1, f3),
+ b3=(f1, f2, f3),
+ b4=(f1, f3),
+ b5=(f1, f2, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [1{b1, b2, b3, b4, b5}, 24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}], usedBy: [2], needs: []},
+ f2: {units: [12{b1, b3, b5, b4, b2}, 8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 31{b5}, 6{b1, b4}, 29{b4}, 4{b1, b3}, 25{b3}, 3{b1, b2}, 2{b1}, 17{b2}], usedBy: [], needs: [1]}],
+ steps=[
+ b1=(f1, f2),
+ b2=(f1, f2),
+ b3=(f1, f2),
+ b4=(f1, f2),
+ b5=(f1, f2)]
+*/
+
import 'lib1.dart';
import 'lib2.dart';
import 'lib3.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/marker.options b/pkg/compiler/test/deferred_loading/data/marker.options
index 1415216..95010cc 100644
--- a/pkg/compiler/test/deferred_loading/data/marker.options
+++ b/pkg/compiler/test/deferred_loading/data/marker.options
@@ -1 +1,3 @@
spec=pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+two-frag=pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+three-frag=pkg/compiler/test/deferred_loading/deferred_loading_test.dart
diff --git a/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart b/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart
index d10d45f..7c1acac 100644
--- a/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart
@@ -4,6 +4,10 @@
// @dart = 2.7
+/*library:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
import 'lib.dart' deferred as lib;
/*member: main:member_unit=main{}*/
diff --git a/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart b/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart
index dfbc3c4..486907a 100644
--- a/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{libb}], usedBy: [], needs: []}],
+ steps=[libb=(f1)]
+*/
+
// @dart = 2.7
import 'libb.dart' deferred as libb;
import 'libc.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart b/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart
index 8337bc0..dea65bc 100644
--- a/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart
@@ -2,6 +2,13 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+/*library:
+ output_units=[f1: {units: [1{s1, s2}], usedBy: [], needs: []}],
+ steps=[
+ s1=(f1),
+ s2=(f1)]
+*/
+
// @dart = 2.7
/// Regression test for issue https://github.com/dart-lang/sdk/issues/31306.
diff --git a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
index 78918bf..78990e9 100644
--- a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: [1]},
+ f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [2{lib1, lib2}, 1{lib1}], usedBy: [2], needs: []},
+ f2: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1),
+ lib2=(f1, f2)]
+*/
+
// @dart = 2.7
// The class lib1.C is referenced via lib1
diff --git a/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart b/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart
index c279b65..6049f85 100644
--- a/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{c}], usedBy: [], needs: []}],
+ steps=[c=(f1)]
+*/
+
// @dart = 2.7
import 'lib1.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
index dd5873b..4cad65c 100644
--- a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
@@ -2,6 +2,25 @@
// 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.
+/*spec|three-frag.library:
+ output_units=[
+ f1: {units: [3{lib1, lib3}], usedBy: [2, 3], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: [1]},
+ f3: {units: [2{lib3}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1, f2),
+ lib3=(f1, f3)]
+*/
+
+/*two-frag.library:
+ output_units=[
+ f1: {units: [3{lib1, lib3}, 2{lib3}], usedBy: [2], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: [1]}],
+ steps=[
+ lib1=(f1, f2),
+ lib3=(f1)]
+*/
+
// @dart = 2.7
import 'lib1.dart' deferred as lib1;
diff --git a/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart b/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart
index 8b8ad89..3ee33186 100644
--- a/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart
@@ -2,6 +2,11 @@
// 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:
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
// @dart = 2.7
import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/deferred_loading_test.dart b/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
index 43f6ffa..e261408 100644
--- a/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+++ b/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
@@ -15,6 +15,9 @@
import 'package:compiler/src/ir/util.dart';
import 'package:compiler/src/js_model/element_map.dart';
import 'package:compiler/src/js_model/js_world.dart';
+import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_emitter/startup_emitter/fragment_merger.dart';
+import 'package:compiler/src/kernel/kernel_strategy.dart';
import 'package:expect/expect.dart';
import '../equivalence/id_equivalence.dart';
import '../equivalence/id_equivalence_helper.dart';
@@ -37,7 +40,9 @@
await checkTests(dataDir, const OutputUnitDataComputer(),
options: compilerOptions, args: args, setUpFunction: () {
importPrefixes.clear();
- }, testedConfigs: allSpecConfigs);
+ },
+ testedConfigs: allSpecConfigs +
+ [twoDeferredFragmentConfig, threeDeferredFragmentConfig]);
});
}
@@ -47,10 +52,7 @@
// prefix name responds to two different libraries.
Map<String, Uri> importPrefixes = {};
-/// Create a consistent string representation of [OutputUnit]s for both
-/// KImportEntities and ImportElements.
-String outputUnitString(OutputUnit unit) {
- if (unit == null) return 'none';
+String importPrefixString(OutputUnit unit) {
StringBuffer sb = StringBuffer();
bool first = true;
for (ImportEntity import in unit.importsForTesting) {
@@ -73,15 +75,48 @@
}
importPrefixes[import.name] = import.enclosingLibraryUri;
}
+ return sb.toString();
+}
+
+/// Create a consistent string representation of [OutputUnit]s for both
+/// KImportEntities and ImportElements.
+String outputUnitString(OutputUnit unit) {
+ if (unit == null) return 'none';
+ String sb = importPrefixString(unit);
return '${unit.name}{$sb}';
}
+Map<String, List<PreFragment>> buildPreFragmentMap(
+ Map<String, List<Fragment>> loadMap,
+ List<PreFragment> preDeferredFragments) {
+ Map<DeferredFragment, PreFragment> fragmentMap = {};
+ for (var preFragment in preDeferredFragments) {
+ for (var fragment in preFragment.fragments) {
+ assert(!fragmentMap.containsKey(fragment));
+ fragmentMap[fragment] = preFragment;
+ }
+ }
+
+ Map<String, List<PreFragment>> preFragmentMap = {};
+ loadMap.forEach((loadId, fragments) {
+ Set<PreFragment> preFragments = {};
+ for (var fragment in fragments) {
+ preFragments.add(fragmentMap[fragment]);
+ }
+ assert(!preFragmentMap.containsKey(loadId));
+ preFragmentMap[loadId] = preFragments.toList();
+ });
+ return preFragmentMap;
+}
+
class Tags {
static const String cls = 'class_unit';
static const String member = 'member_unit';
static const String closure = 'closure_unit';
static const String constants = 'constants';
static const String type = 'type_unit';
+ static const String steps = 'steps';
+ static const String outputUnits = 'output_units';
}
class OutputUnitDataComputer extends DataComputer<Features> {
@@ -118,10 +153,82 @@
}
@override
+ void computeLibraryData(Compiler compiler, LibraryEntity library,
+ Map<Id, ActualData<Features>> actualMap,
+ {bool verbose}) {
+ KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
+ ir.Library node = frontendStrategy.elementMap.getLibraryNode(library);
+ List<PreFragment> preDeferredFragments = compiler
+ .backendStrategy.emitterTask.emitter.preDeferredFragmentsForTesting;
+ Program program =
+ compiler.backendStrategy.emitterTask.emitter.programForTesting;
+ Map<String, List<PreFragment>> preFragmentMap =
+ buildPreFragmentMap(program.loadMap, preDeferredFragments);
+ PreFragmentsIrComputer(compiler.reporter, actualMap, preFragmentMap)
+ .computeForLibrary(node);
+ }
+
+ @override
DataInterpreter<Features> get dataValidator =>
const FeaturesDataInterpreter();
}
+class PreFragmentsIrComputer extends IrDataExtractor<Features> {
+ final Map<String, List<PreFragment>> _preFragmentMap;
+
+ PreFragmentsIrComputer(DiagnosticReporter reporter,
+ Map<Id, ActualData<Features>> actualMap, this._preFragmentMap)
+ : super(reporter, actualMap);
+
+ @override
+ Features computeLibraryValue(Id id, ir.Library library) {
+ var name = '${library.importUri.pathSegments.last}';
+ Features features = new Features();
+ if (!name.startsWith('main')) return features;
+
+ int index = 1;
+ Map<PreFragment, int> preFragmentIndices = {};
+ Map<int, PreFragment> reversePreFragmentIndices = {};
+ _preFragmentMap.forEach((loadId, preFragments) {
+ List<String> preFragmentNeeds = [];
+ for (var preFragment in preFragments) {
+ if (!preFragmentIndices.containsKey(preFragment)) {
+ preFragmentIndices[preFragment] = index;
+ reversePreFragmentIndices[index++] = preFragment;
+ }
+ preFragmentNeeds.add('f${preFragmentIndices[preFragment]}');
+ }
+ features.addElement(
+ Tags.steps, '$loadId=(${preFragmentNeeds.join(', ')})');
+ });
+
+ for (int i = 1; i < index; i++) {
+ var preFragment = reversePreFragmentIndices[i];
+ List<int> needs = [];
+ List<OutputUnit> supplied = [];
+ List<int> usedBy = [];
+ for (var dependent in preFragment.successors) {
+ assert(preFragmentIndices.containsKey(dependent));
+ usedBy.add(preFragmentIndices[dependent]);
+ }
+
+ for (var dependency in preFragment.predecessors) {
+ assert(preFragmentIndices.containsKey(dependency));
+ needs.add(preFragmentIndices[dependency]);
+ }
+
+ for (var fragment in preFragment.fragments) {
+ supplied.add(fragment.outputUnit);
+ }
+ var suppliedString = '[${supplied.map(outputUnitString).join(', ')}]';
+ features.addElement(Tags.outputUnits,
+ 'f$i: {units: $suppliedString, usedBy: $usedBy, needs: $needs}');
+ }
+
+ return features;
+ }
+}
+
class OutputUnitIrComputer extends IrDataExtractor<Features> {
final JsToElementMap _elementMap;
final OutputUnitData _data;
diff --git a/pkg/compiler/test/equivalence/id_equivalence_helper.dart b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
index 0a9c8d6..1e22b7c 100644
--- a/pkg/compiler/test/equivalence/id_equivalence_helper.dart
+++ b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
@@ -29,12 +29,24 @@
const String specMarker = 'spec';
const String prodMarker = 'prod';
+const String twoDeferredFragmentMarker = 'two-frag';
+const String threeDeferredFragmentMarker = 'three-frag';
const TestConfig specConfig = TestConfig(specMarker, 'compliance mode', []);
const TestConfig prodConfig = TestConfig(prodMarker, 'production mode',
[Flags.omitImplicitChecks, Flags.laxRuntimeTypeToString]);
+const TestConfig twoDeferredFragmentConfig = TestConfig(
+ twoDeferredFragmentMarker,
+ 'two deferred fragment mode',
+ ['${Flags.mergeFragmentsThreshold}=2']);
+
+const TestConfig threeDeferredFragmentConfig = TestConfig(
+ threeDeferredFragmentMarker,
+ 'three deferred fragment mode',
+ ['${Flags.mergeFragmentsThreshold}=3']);
+
/// Default internal configurations not including experimental features.
const List<TestConfig> defaultInternalConfigs = [specConfig, prodConfig];
diff --git a/pkg/compiler/test/js/js_size_estimator_test.dart b/pkg/compiler/test/js/js_size_estimator_test.dart
index cb5ce76..e9fbfcf 100644
--- a/pkg/compiler/test/js/js_size_estimator_test.dart
+++ b/pkg/compiler/test/js/js_size_estimator_test.dart
@@ -24,7 +24,7 @@
// Always verify the actual results from the [SizeEstimator].
// This is the actual test, though DebugSizeEstimator is pretty trivial.
- int actualEstimate = EstimateSize(node);
+ int actualEstimate = estimateSize(node);
Expect.equals(actualEstimate, debugSizeEstimator.charCount);
return debugSizeEstimator;
}
diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart
index f6fa451..d3ef96e 100644
--- a/pkg/dartdev/lib/src/commands/fix.dart
+++ b/pkg/dartdev/lib/src/commands/fix.dart
@@ -19,22 +19,18 @@
static final NumberFormat _numberFormat = NumberFormat.decimalPattern();
- static const String cmdDescription = '''Fix Dart source code.
+ static const String cmdDescription =
+ '''Apply automated fixes to Dart source code.
-This tool looks for and fixes analysis issues that have associated automated fixes or issues that have associated package API migration information. See dart.dev/go/dart-fix for more information about how automated package API changes work.
+This tool looks for and fixes analysis issues that have associated automated fixes.
-To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed changes for a project, or ['dart fix --apply'] to apply the changes.''';
+To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed changes for a project, or ['dart fix --apply'] to apply the changes.
- @override
- String get description {
- if (log != null && log.ansi.useAnsi) {
- return cmdDescription
- .replaceAll('[', log.ansi.bold)
- .replaceAll(']', log.ansi.none);
- } else {
- return cmdDescription.replaceAll('[', '').replaceAll(']', '');
- }
- }
+[Note:] $disclaimer''';
+
+ static const disclaimer = 'The `fix` command is under development and '
+ 'subject to change before the next stable release. Feedback is welcome - '
+ 'please file at https://github.com/dart-lang/sdk/issues.';
// This command is hidden as it's currently experimental.
FixCommand({bool verbose = false})
@@ -61,24 +57,34 @@
}
@override
- FutureOr<int> run() async {
- log.stdout('\n${log.ansi.emphasized('Note:')} The `fix` command is '
- 'provisional and subject to change or removal in future releases.\n');
+ String get description {
+ if (log != null && log.ansi.useAnsi) {
+ return cmdDescription
+ .replaceAll('[', log.ansi.bold)
+ .replaceAll(']', log.ansi.none);
+ } else {
+ return cmdDescription.replaceAll('[', '').replaceAll(']', '');
+ }
+ }
+ @override
+ FutureOr<int> run() async {
var dryRun = argResults['dry-run'];
var testMode = argResults['compare-to-golden'];
var apply = argResults['apply'];
+ if (!apply && !dryRun && !testMode) {
+ printUsage();
+ return 0;
+ }
+
+ log.stdout('\n${log.ansi.emphasized('Note:')} $disclaimer\n');
+
var arguments = argResults.rest;
var argumentCount = arguments.length;
if (argumentCount > 1) {
usageException('Only one file or directory is expected.');
}
- if (!apply && !dryRun && !testMode) {
- printUsage();
- return 0;
- }
-
var dir =
argumentCount == 0 ? io.Directory.current : io.Directory(arguments[0]);
if (!dir.existsSync()) {
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 8a92d7b..74ff1e4 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:cli_util/cli_logging.dart';
+import 'package:dartdev/src/commands/fix.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
@@ -34,6 +35,18 @@
return p.runSync(['fix', ...args], workingDir: workingDir);
}
+ test('help', () {
+ p = project(mainSrc: 'int get foo => 1;\n');
+
+ var result = runFix([p.dirPath, '--help']);
+
+ expect(result.exitCode, 0);
+ expect(result.stderr, isEmpty);
+ expect(result.stdout, contains(FixCommand.disclaimer));
+ expect(
+ result.stdout, contains('Apply automated fixes to Dart source code.'));
+ });
+
test('none', () {
p = project(mainSrc: 'int get foo => 1;\n');
@@ -41,7 +54,9 @@
expect(result.exitCode, 0);
expect(result.stderr, isEmpty);
- expect(result.stdout, contains('Fix Dart source code.'));
+ expect(result.stdout, contains(FixCommand.disclaimer));
+ expect(
+ result.stdout, contains('Apply automated fixes to Dart source code.'));
});
test('--apply (none)', () {
@@ -51,6 +66,7 @@
expect(result.exitCode, 0);
expect(result.stderr, isEmpty);
+ expect(result.stdout, contains(FixCommand.disclaimer));
expect(result.stdout, contains('Nothing to fix!'));
});
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 2071a31..92fa101 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6095,8 +6095,9 @@
if (error) {
CHECK_NULL(error_message);
- unit.CompleteLoad(String::Handle(String::New(error_message)),
- transient_error);
+ return Api::NewHandle(
+ T, unit.CompleteLoad(String::Handle(String::New(error_message)),
+ transient_error));
} else {
#if defined(SUPPORT_TIMELINE)
TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
@@ -6120,10 +6121,8 @@
return Api::NewHandle(T, error.raw());
}
- unit.CompleteLoad(String::Handle(), false);
+ return Api::NewHandle(T, unit.CompleteLoad(String::Handle(), false));
}
-
- return Api::Success();
}
DART_EXPORT Dart_Handle
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 57ff8c6..e673c0a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -17481,8 +17481,8 @@
return Isolate::Current()->CallDeferredLoadHandler(id());
}
-void LoadingUnit::CompleteLoad(const String& error_message,
- bool transient_error) const {
+ObjectPtr LoadingUnit::CompleteLoad(const String& error_message,
+ bool transient_error) const {
ASSERT(!loaded());
ASSERT(load_outstanding());
set_loaded(error_message.IsNull());
@@ -17496,12 +17496,7 @@
args.SetAt(0, Smi::Handle(Smi::New(id())));
args.SetAt(1, error_message);
args.SetAt(2, Bool::Get(transient_error));
- const Object& result = Object::Handle(DartEntry::InvokeFunction(func, args));
- if (result.IsUnwindError()) {
- Thread::Current()->set_sticky_error(Error::Cast(result));
- } else if (result.IsError()) {
- UNREACHABLE();
- }
+ return DartEntry::InvokeFunction(func, args);
}
const char* Error::ToErrorCString() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 467e1b1..2a3b73d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -6996,7 +6996,8 @@
}
ObjectPtr IssueLoad() const;
- void CompleteLoad(const String& error_message, bool transient_error) const;
+ ObjectPtr CompleteLoad(const String& error_message,
+ bool transient_error) const;
private:
FINAL_HEAP_OBJECT_IMPLEMENTATION(LoadingUnit, Object);
diff --git a/sdk/lib/_internal/vm/lib/lib_prefix.dart b/sdk/lib/_internal/vm/lib/lib_prefix.dart
index 4259640..7604f62 100644
--- a/sdk/lib/_internal/vm/lib/lib_prefix.dart
+++ b/sdk/lib/_internal/vm/lib/lib_prefix.dart
@@ -31,7 +31,11 @@
@pragma("vm:entry-point")
void _completeLoads(Object unit, String? errorMessage, bool transientError) {
- Completer<void> load = _LibraryPrefix._loads[unit]!;
+ Completer<void>? load = _LibraryPrefix._loads[unit];
+ if (load == null) {
+ // Embedder loaded even though prefix.loadLibrary() wasn't called.
+ _LibraryPrefix._loads[unit] = load = new Completer<void>();
+ }
if (errorMessage == null) {
load.complete(null);
} else {
diff --git a/tests/dart2js/internal/deferred/load_in_correct_order_test.dart b/tests/dart2js/internal/deferred/load_in_correct_order_test.dart
index dc6727d..4f04a2c 100644
--- a/tests/dart2js/internal/deferred/load_in_correct_order_test.dart
+++ b/tests/dart2js/internal/deferred/load_in_correct_order_test.dart
@@ -74,7 +74,25 @@
// This test has 3 loadLibrary calls, this array contains how many hunks will be
// loaded by each call.
self.currentLoadLibraryCall = 0;
-self.filesPerLoadLibraryCall = [4, 2, 1];
+self.filesPerLoadLibraryCall = null;
+
+self.initFilesPerLoadLibraryCall = function() {
+ // We assume we load d1, then d2, then d3.
+ var loadOrder = ['d1', 'd2', 'd3'];
+ var uniques = {};
+ self.filesPerLoadLibraryCall = [];
+ for (var i = 0; i < loadOrder.length; i++) {
+ var filesToLoad = 0;
+ var parts = init.deferredLibraryParts[loadOrder[i]];
+ for (var j = 0; j < parts.length; j++) {
+ if (!uniques.hasOwnProperty(parts[j])) {
+ uniques[parts[j]] = true;
+ filesToLoad++;
+ }
+ }
+ self.filesPerLoadLibraryCall.push(filesToLoad);
+ }
+};
// Download uri via an XHR
self.download = function(uri) {
@@ -99,6 +117,9 @@
// Hook to control how we load hunks (we force them to be out of order).
self.dartDeferredLibraryLoader = function(uri, success, error) {
+ if (self.filesPerLoadLibraryCall == null) {
+ self.initFilesPerLoadLibraryCall();
+ }
self.uris.push(uri);
self.successCallbacks.push(success);
if (isD8) {
@@ -111,13 +132,13 @@
// Do the actual load of the hunk and call the corresponding success callback.
self.doLoad = function(i) {
self.setTimeout(function () {
- var uri = self.uris[i];
- if (self.isD8) {
- load(uri);
- } else {
- eval(self.content[uri]);
- }
- (self.successCallbacks[i])();
+ var uri = self.uris[i];
+ if (self.isD8) {
+ load(uri);
+ } else {
+ eval(self.content[uri]);
+ }
+ (self.successCallbacks[i])();
}, 0);
};
@@ -125,13 +146,10 @@
// purposely load the hunks out of order.
self.doActualLoads = function() {
self.currentLoadLibraryCall++;
- if (self.total == 4) {
- self.doLoad(3); // load purposely out of order!
- self.doLoad(0);
- self.doLoad(1);
- self.doLoad(2);
- } else {
- for (var i = 0; i < self.total; i++) {
+ if (self.total >= 1) {
+ // Load out of order, last first.
+ self.doLoad(self.total - 1);
+ for (var i = 0; i < self.total - 1; i++) {
self.doLoad(i);
}
}
diff --git a/tools/VERSION b/tools/VERSION
index 8594b2d..d10270e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 133
+PRERELEASE 134
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 4b3cf6e..79fedf6 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -642,6 +642,18 @@
"host-checked": true
}
},
+ "dart2js-hostasserts-weak-max-fragments-(linux|win)-x64-(d8|chrome)": {
+ "options": {
+ "builder-tag": "dart2js-weak",
+ "dart2js-options": [
+ "--libraries-spec=sdk/lib/libraries.json",
+ "--platform-binaries=out/ReleaseX64/",
+ "--merge-fragments-threshold=3"
+ ],
+ "timeout": 240,
+ "host-checked": true
+ }
+ },
"dart2js-hostasserts-weak-mac-x64-(d8|chrome)": {
"options": {
"builder-tag": "dart2js-weak",
@@ -664,6 +676,19 @@
"host-checked": true
}
},
+ "dart2js-hostasserts-strong-max-fragments-(linux|win)-x64-(d8|chrome)": {
+ "options": {
+ "builder-tag": "dart2js-strong",
+ "dart2js-options": [
+ "--libraries-spec=sdk/lib/libraries.json",
+ "--platform-binaries=out/ReleaseX64/",
+ "--merge-fragments-threshold=3"
+ ],
+ "timeout": 240,
+ "host-checked": true
+ }
+ },
+
"dart2js-hostasserts-strong-mac-x64-(d8|chrome)": {
"options": {
"builder-tag": "dart2js-strong",
@@ -2469,6 +2494,16 @@
"fileset": "web_platform_hostasserts_nnbd"
},
{
+ "name": "dart2js nnbd weak d8 fragment merging tests",
+ "arguments": [
+ "-ndart2js-hostasserts-weak-max-fragments-linux-x64-d8",
+ "--dart2js-batch",
+ "dart2js/deferred/"
+ ],
+ "shards": 1,
+ "fileset": "web_platform_hostasserts_nnbd"
+ },
+ {
"name": "dart2js nnbd weak chrome tests",
"arguments": [
"-ndart2js-hostasserts-weak-linux-x64-chrome",
@@ -2510,6 +2545,16 @@
"fileset": "web_platform_hostasserts_nnbd"
},
{
+ "name": "dart2js nnbd strong d8 fragment merging tests",
+ "arguments": [
+ "-ndart2js-hostasserts-strong-max-fragments-linux-x64-d8",
+ "--dart2js-batch",
+ "dart2js/deferred/"
+ ],
+ "shards": 1,
+ "fileset": "web_platform_hostasserts_nnbd"
+ },
+ {
"name": "dart2js nnbd strong chrome tests",
"arguments": [
"-ndart2js-hostasserts-strong-linux-x64-chrome",