blob: 75fe53f8d35e088302019713c77012ab27f85f7c [file]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:typed_data';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/name_union.dart';
import 'package:analyzer/src/fine/library_manifest.dart';
import 'package:analyzer/src/fine/requirements.dart';
import 'package:analyzer/src/summary2/bundle_writer.dart';
import 'package:analyzer/src/summary2/detach_nodes.dart';
import 'package:analyzer/src/summary2/enclosing_type_parameters_flag.dart';
import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/extension_type.dart';
import 'package:analyzer/src/summary2/library_builder.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/summary2/simply_bounded.dart';
import 'package:analyzer/src/summary2/super_constructor_resolver.dart';
import 'package:analyzer/src/summary2/top_level_inference.dart';
import 'package:analyzer/src/summary2/type_alias.dart';
import 'package:analyzer/src/summary2/types_builder.dart';
import 'package:analyzer/src/summary2/variance_builder.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/utilities/uri_cache.dart';
LinkResult link({
required LinkedElementFactory elementFactory,
required OperationPerformanceImpl performance,
required List<LibraryFileKind> inputLibraries,
required String apiSignature,
}) {
var linker = Linker(
elementFactory: elementFactory,
apiSignature: apiSignature,
);
linker.link(performance: performance, inputLibraries: inputLibraries);
return LinkResult(resolutionBytes: linker.resolutionBytes);
}
/// Information about a declaring formal parameter, used while linking.
class DeclaringFormalParameterInfo {
final ast.FormalParameterImpl node;
final FieldFragmentImpl fieldFragment;
final FormalParameterFragmentImpl formalFragment;
DeclaringFormalParameterInfo({
required this.node,
required this.fieldFragment,
required this.formalFragment,
});
}
class Linker {
final LinkedElementFactory elementFactory;
final String apiSignature;
/// Libraries that are being linked.
final Map<Uri, LibraryBuilder> builders = {};
final Map<FragmentImpl, ast.AstNode> _fragmentNodes = Map.identity();
final Map<ast.FormalParameterImpl, DeclaringFormalParameterInfo>
declaringFormalParameters = Map.identity();
late InheritanceManager3 inheritance; // TODO(scheglov): cache it
Map<Uri, LibraryManifest> newLibraryManifests = {};
late Uint8List resolutionBytes;
Linker({required this.elementFactory, required this.apiSignature});
AnalysisContextImpl get analysisContext {
return elementFactory.analysisContext;
}
DeclaredVariables get declaredVariables {
return analysisContext.declaredVariables;
}
RootReference get rootReference => elementFactory.rootReference;
bool get _isLinkingDartCore {
var dartCoreUri = uriCache.parse('dart:core');
return builders.containsKey(dartCoreUri);
}
/// If the [fragment] is part of a library being linked, return the node
/// from which it was created.
ast.AstNode? getLinkingNode(FragmentImpl fragment) {
return _fragmentNodes[fragment];
}
bool isLinkingElement(Element element) {
return builders.containsKey(element.library?.uri);
}
void link({
required OperationPerformanceImpl performance,
required List<LibraryFileKind> inputLibraries,
}) {
performance.run('LibraryBuilder.build', (performance) {
for (var inputLibrary in inputLibraries) {
LibraryBuilder.build(
linker: this,
inputLibrary: inputLibrary,
performance: performance,
);
}
});
performance.run('buildOutlines', (performance) {
_buildOutlines(performance: performance);
});
performance.run('writeLibraries', (performance) {
_writeLibraries(performance: performance);
});
}
void setFragmentNode(FragmentImpl fragment, ast.AstNode node) {
assert(!_fragmentNodes.containsKey(fragment));
_fragmentNodes[fragment] = node;
}
void _buildClassSyntheticConstructors() {
for (var library in builders.values) {
library.buildClassSyntheticConstructors();
}
}
void _buildElementNameUnions() {
for (var builder in builders.values) {
var element = builder.element;
element.nameUnion = ElementNameUnion.forLibrary(element);
}
}
void _buildEnumChildren() {
for (var library in builders.values) {
library.buildEnumChildren();
}
}
void _buildEnumSyntheticConstructors() {
for (var library in builders.values) {
library.buildEnumSyntheticConstructors();
}
}
void _buildExportScopes() {
for (var library in builders.values) {
library.buildInitialExportScope();
}
var exportingBuilders = <LibraryBuilder>{};
var exportedBuilders = <LibraryBuilder>{};
for (var library in builders.values) {
library.addExporters();
}
for (var library in builders.values) {
if (library.exports.isNotEmpty) {
exportedBuilders.add(library);
for (var export in library.exports) {
exportingBuilders.add(export.exporter);
}
}
}
var both = <LibraryBuilder>{};
for (var exported in exportedBuilders) {
if (exportingBuilders.contains(exported)) {
both.add(exported);
}
for (var export in exported.exports) {
exported.exportScope.forEach(export.addToExportScope);
}
}
// We keep a queue of export entries to propagate.
// First we loop over every library that both exports and is exported, but
// then we only process the export entries that should be propagated.
var additionalExports = <_AdditionalExport>[];
void addExport(Export export, ExportEntry entry) {
if (export.addToExportScope(entry)) {
// We've added [entry.name] to [export.exporter]s export scope.
// We need to propagate that to anyone that exports that library.
additionalExports.add(_AdditionalExport(export.exporter, entry));
}
}
for (var exported in both) {
for (var export in exported.exports) {
exported.exportScope.forEach((entry) {
addExport(export, entry);
});
}
}
while (additionalExports.isNotEmpty) {
var additionalExport = additionalExports.removeLast();
for (var export in additionalExport.exportedLibrary.exports) {
addExport(export, additionalExport.entry);
}
}
assert(() {
for (var exported in both) {
for (var export in exported.exports) {
exported.exportScope.forEach((entry) {
if (export.addToExportScope(entry)) {
throw "Error in export calculation: Assert failed.";
}
});
}
}
return true;
}(), true);
for (var library in builders.values) {
library.storeExportScope();
}
}
void _buildOutlines({required OperationPerformanceImpl performance}) {
_createTypeSystemIfNotLinkingDartCore();
performance.run('computeLibraryScopes', (performance) {
_computeLibraryScopes(performance: performance);
});
globalResultRequirements?.addExcludedLibraries(
builders.values.map((builder) => builder.uri),
);
_createTypeSystem();
_resolveTypes();
_computeHasNonFinalField();
_setDefaultSupertypes();
_buildClassSyntheticConstructors();
_buildEnumSyntheticConstructors();
_replaceConstFieldsIfNoConstConstructor();
_resolveConstructorFieldFormals();
_buildEnumChildren();
_computeFieldPromotability();
SuperConstructorResolver(this).perform();
_performTopLevelInference();
buildExtensionTypes(this);
_resolveConstructors();
_computeRedirectingConstructorCycles();
_resolveConstantInitializers();
_resolveDefaultValues();
_resolveMetadata();
_collectMixinSuperInvokedNames();
EnclosingTypeParameterReferenceFlag(this).perform();
_buildElementNameUnions();
_detachNodes();
}
void _collectMixinSuperInvokedNames() {
for (var library in builders.values) {
library.collectMixinSuperInvokedNames();
}
}
void _computeFieldPromotability() {
for (var library in builders.values) {
library.computeFieldPromotability();
}
}
/// Set [InterfaceElementImpl.hasNonFinalField] for classes and mixins.
///
/// We actually use it only for classes (which can have `const` constructors),
/// but mixins contribute to it.
void _computeHasNonFinalField() {
var linkingElements = builders.values
.expand((builder) => builder.element.children)
.whereType<InterfaceElementImpl>()
.toSet();
var alreadyComputed = Set<InterfaceElementImpl>.identity();
bool computeFor(InterfaceElementImpl element) {
if (!linkingElements.contains(element) || !alreadyComputed.add(element)) {
return element.hasNonFinalField;
}
var hasNonFinalField = [
element.supertype,
...element.mixins,
].nonNulls.any((type) => computeFor(type.element));
hasNonFinalField |= !element.fields
.where((field) => field.isInstanceField)
.every((field) {
// If has storage...
if ((field.isOriginDeclaration && !field.isAbstract) ||
field.isOriginDeclaringFormalParameter) {
// ...then must be final
return field.isFinal || field.isConst;
}
// Otherwise we don't care.
return true;
});
return element.hasNonFinalField = hasNonFinalField;
}
for (var element in linkingElements) {
computeFor(element);
}
}
void _computeLibraryScopes({required OperationPerformanceImpl performance}) {
globalResultRequirements.untracked(
reason: 'No complete elements yet',
operation: () {
for (var library in builders.values) {
library.buildElements();
}
},
);
_buildExportScopes();
}
void _computeRedirectingConstructorCycles() {
var visited = Set<ConstructorElementImpl>.identity();
for (var builder in builders.values) {
for (var element in builder.element.children) {
if (element is InterfaceElementImpl) {
for (var constructor in element.constructors) {
if (visited.contains(constructor)) {
continue;
}
if (constructor.redirectedConstructor == null) {
continue;
}
var indexOf = Map<ConstructorElementImpl, int>.identity();
var path = <ConstructorElementImpl>[];
ConstructorElementImpl? current = constructor;
while (current != null) {
var cycleStart = indexOf[current];
if (cycleStart != null) {
for (var i = cycleStart; i < path.length; i++) {
path[i].isInRedirectingConstructorCycle = true;
}
break;
}
if (visited.contains(current)) {
break;
}
indexOf[current] = path.length;
path.add(current);
visited.add(current);
current = current.redirectedConstructor?.baseElement;
}
}
}
}
}
}
void _createTypeSystem() {
elementFactory.createTypeProviders(
elementFactory.dartCoreElement,
elementFactory.dartAsyncElement,
);
inheritance = InheritanceManager3();
}
/// To resolve macro annotations we need to access exported namespaces of
/// imported (and already linked) libraries. While computing it we might
/// need `Null` from `dart:core` (to convert null safe types to legacy).
void _createTypeSystemIfNotLinkingDartCore() {
if (!_isLinkingDartCore) {
_createTypeSystem();
}
}
void _detachNodes() {
for (var builder in builders.values) {
detachElementsFromNodes(builder.element);
}
}
void _performTopLevelInference() {
TopLevelInference(this).infer();
}
void _replaceConstFieldsIfNoConstConstructor() {
for (var library in builders.values) {
library.replaceConstFieldsIfNoConstConstructor();
}
}
void _resolveConstantInitializers() {
ConstantInitializersResolver(this).perform();
}
void _resolveConstructorFieldFormals() {
for (var library in builders.values) {
library.resolveConstructorFieldFormals();
}
}
void _resolveConstructors() {
for (var library in builders.values) {
library.resolveConstructors();
}
}
void _resolveDefaultValues() {
for (var library in builders.values) {
library.resolveDefaultValues();
}
}
void _resolveMetadata() {
for (var library in builders.values) {
library.resolveMetadata();
}
}
void _resolveTypes() {
var nodesToBuildType = NodesToBuildType();
for (var library in builders.values) {
library.resolveTypes(nodesToBuildType);
}
VarianceBuilder(this).perform();
computeSimplyBounded(this);
TypeAliasSelfReferenceFinder().perform(this);
TypesBuilder(this).build(nodesToBuildType);
}
void _setDefaultSupertypes() {
for (var library in builders.values) {
library.setDefaultSupertypes();
}
}
void _writeLibraries({required OperationPerformanceImpl performance}) {
var bundleWriter = BundleWriter();
for (var builder in builders.values) {
bundleWriter.writeLibraryElement(builder.element);
}
var writeWriterResult = performance.run('bundleWriteFinish', (performance) {
return bundleWriter.finish();
});
resolutionBytes = writeWriterResult.resolutionBytes;
performance.getDataInt('length').add(resolutionBytes.length);
}
}
class LinkResult {
final Uint8List resolutionBytes;
LinkResult({required this.resolutionBytes});
}
class _AdditionalExport {
final LibraryBuilder exportedLibrary;
final ExportEntry entry;
_AdditionalExport(this.exportedLibrary, this.entry);
}