| // 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(); | |
| 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; | |
| } | |
| InheritanceManager3 get inheritance { | |
| return elementFactory.analysisSession.inheritanceManager; | |
| } | |
| 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(); | |
| _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) { | |
| visited.add(constructor); | |
| continue; | |
| } | |
| var path = <ConstructorElementImpl>[]; | |
| ConstructorElementImpl? current = constructor; | |
| while (current != null && visited.add(current)) { | |
| path.add(current); | |
| current = current.redirectedConstructor?.baseElement; | |
| } | |
| if (current != null) { | |
| var isInCycle = false; | |
| for (var element in path) { | |
| if (identical(element, current)) { | |
| isInCycle = true; | |
| } | |
| if (isInCycle) { | |
| element.isInRedirectingConstructorCycle = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void _createTypeSystem() { | |
| elementFactory.createTypeProviders( | |
| elementFactory.dartCoreElement, | |
| elementFactory.dartAsyncElement, | |
| ); | |
| } | |
| /// 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 _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); | |
| } |