| // 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:_fe_analyzer_shared/src/field_promotability.dart'; | |
| import 'package:analyzer/dart/analysis/features.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/src/dart/analysis/file_state.dart' as file_state; | |
| import 'package:analyzer/src/dart/analysis/file_state.dart' hide DirectiveUri; | |
| import 'package:analyzer/src/dart/analysis/unlinked_data.dart'; | |
| import 'package:analyzer/src/dart/ast/ast.dart' as ast; | |
| import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart'; | |
| import 'package:analyzer/src/dart/element/element.dart'; | |
| import 'package:analyzer/src/dart/element/field_name_non_promotability_info.dart' | |
| as element_model; | |
| import 'package:analyzer/src/dart/resolver/scope.dart'; | |
| import 'package:analyzer/src/summary2/combinator.dart'; | |
| import 'package:analyzer/src/summary2/constructor_initializer_resolver.dart'; | |
| import 'package:analyzer/src/summary2/default_value_resolver.dart'; | |
| import 'package:analyzer/src/summary2/element_builder.dart'; | |
| import 'package:analyzer/src/summary2/export.dart'; | |
| import 'package:analyzer/src/summary2/informative_data.dart'; | |
| import 'package:analyzer/src/summary2/link.dart'; | |
| import 'package:analyzer/src/summary2/linked_element_factory.dart'; | |
| import 'package:analyzer/src/summary2/metadata_resolver.dart'; | |
| import 'package:analyzer/src/summary2/reference.dart'; | |
| import 'package:analyzer/src/summary2/reference_resolver.dart'; | |
| import 'package:analyzer/src/summary2/types_builder.dart'; | |
| import 'package:analyzer/src/util/performance/operation_performance.dart'; | |
| import 'package:analyzer/src/utilities/extensions/collection.dart'; | |
| import 'package:collection/collection.dart'; | |
| /// Builds the URI of the library imported by [state]. | |
| /// | |
| /// The imported library, if any, must be already loaded into the | |
| /// [elementFactory]. | |
| DirectiveUri buildLibraryImportUri({ | |
| required LibraryImportState state, | |
| required LinkedElementFactory elementFactory, | |
| }) { | |
| switch (state) { | |
| case LibraryImportWithFile(): | |
| var importedLibraryKind = state.importedLibrary; | |
| if (importedLibraryKind != null) { | |
| var importedFile = importedLibraryKind.file; | |
| var importedUri = importedFile.uri; | |
| var importedLibrary = elementFactory.libraryOfUri2(importedUri); | |
| return DirectiveUriWithLibraryImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: importedLibrary.source, | |
| library: importedLibrary, | |
| ); | |
| } else { | |
| return DirectiveUriWithSourceImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: state.importedSource, | |
| ); | |
| } | |
| case LibraryImportWithInSummarySource(): | |
| var importedLibrarySource = state.importedLibrarySource; | |
| if (importedLibrarySource != null) { | |
| var importedUri = importedLibrarySource.uri; | |
| var importedLibrary = elementFactory.libraryOfUri2(importedUri); | |
| return DirectiveUriWithLibraryImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: importedLibrary.source, | |
| library: importedLibrary, | |
| ); | |
| } else { | |
| return DirectiveUriWithSourceImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: state.importedSource, | |
| ); | |
| } | |
| default: | |
| var selectedUri = state.selectedUri; | |
| switch (selectedUri) { | |
| case file_state.DirectiveUriWithUri(): | |
| return DirectiveUriWithRelativeUriImpl( | |
| relativeUriString: selectedUri.relativeUriStr, | |
| relativeUri: selectedUri.relativeUri, | |
| ); | |
| case file_state.DirectiveUriWithString(): | |
| return DirectiveUriWithRelativeUriStringImpl( | |
| relativeUriString: selectedUri.relativeUriStr, | |
| ); | |
| default: | |
| return DirectiveUriImpl(); | |
| } | |
| } | |
| } | |
| /// Builds the element model combinators from the unlinked [combinators]. | |
| List<NamespaceCombinator> buildNamespaceCombinators( | |
| List<UnlinkedCombinator> combinators, | |
| ) { | |
| return combinators.map((unlinked) { | |
| if (unlinked.isShow) { | |
| return ShowElementCombinatorImpl() | |
| ..offset = unlinked.keywordOffset | |
| ..end = unlinked.endOffset | |
| ..shownNames = unlinked.names; | |
| } else { | |
| return HideElementCombinatorImpl() | |
| ..offset = unlinked.keywordOffset | |
| ..end = unlinked.endOffset | |
| ..hiddenNames = unlinked.names; | |
| } | |
| }).toFixedList(); | |
| } | |
| class DefiningLinkingUnit extends LinkingUnit { | |
| DefiningLinkingUnit({ | |
| required super.node, | |
| required super.fragment, | |
| required super.informativeBytes, | |
| }); | |
| } | |
| class ImplicitEnumNodes { | |
| final EnumFragmentImpl fragment; | |
| final ast.NamedTypeImpl valuesTypeNode; | |
| final ast.VariableDeclarationImpl valuesNode; | |
| final FieldFragmentImpl valuesFragment; | |
| final Set<String> valuesNames; | |
| ast.ListLiteralImpl valuesInitializer; | |
| ImplicitEnumNodes({ | |
| required this.fragment, | |
| required this.valuesTypeNode, | |
| required this.valuesNode, | |
| required this.valuesFragment, | |
| required this.valuesNames, | |
| required this.valuesInitializer, | |
| }); | |
| } | |
| class LibraryBuilder { | |
| final Linker linker; | |
| final LibraryFileKind kind; | |
| final Uri uri; | |
| final LibraryReference reference; | |
| final LibraryElementImpl element; | |
| final List<LinkingUnit> units; | |
| /// The files that already contribute a fragment to the library: the | |
| /// defining file, and the part files included so far. | |
| /// | |
| /// Repeated part directives retain their sources, but only the first | |
| /// inclusion contributes a fragment and declarations to the library. The | |
| /// defining file is here too, because when a part file is analyzed as a | |
| /// library (no library includes it), its part directives can lead back to | |
| /// itself through a cycle of parts. | |
| late final Set<FileState> _includedFiles = {kind.file}; | |
| final Map<EnumFragmentImpl, ImplicitEnumNodes> implicitEnumNodes = | |
| Map.identity(); | |
| /// Top fragments, in the same order as in AST. | |
| final Map<LibraryFragmentImpl, List<FragmentImpl>> _topFragments = {}; | |
| /// Key: a parent fragment, e.g. [ClassFragmentImpl]. | |
| /// Value: fragments of its direct children. | |
| /// | |
| /// For example `class A { void foo() {} }` has `foo` as child of `A`. | |
| final Map<FragmentImpl, List<FragmentImpl>> _parentChildFragments = | |
| Map.identity(); | |
| /// Local declarations. | |
| final Map<String, ExportableReference> _declaredReferences = {}; | |
| /// The export scope of the library. | |
| ExportScope exportScope = ExportScope(); | |
| /// The `export` directives that export this library. | |
| final List<Export> exports = []; | |
| late final LibraryReferenceBuilder references = LibraryReferenceBuilder( | |
| reference, | |
| ); | |
| /// The identifier used for unnamed local constructs such as prefixes. | |
| int _nextLocalReferenceId = 0; | |
| /// The fields that were speculatively created as [FieldFragmentImpl], | |
| /// but we want to clear [VariableFragmentImpl.constantInitializer2] for it | |
| /// if the class will not end up with a `const` constructor. We don't know | |
| /// at the time when we create them, because of future augmentations. | |
| final Set<FieldFragmentImpl> finalInstanceFields = Set.identity(); | |
| LibraryBuilder._({ | |
| required this.linker, | |
| required this.kind, | |
| required this.uri, | |
| required this.reference, | |
| required this.element, | |
| required this.units, | |
| }); | |
| void addChildFragment(FragmentImpl parent, FragmentImpl child) { | |
| child.enclosingFragment = parent; | |
| (_parentChildFragments[parent] ??= []).add(child); | |
| } | |
| void addExporters() { | |
| for (var (fragmentIndex, fragment) in element.internal.fragments.indexed) { | |
| for (var (exportIndex, exportElement) | |
| in fragment.libraryExports.indexed) { | |
| var exportedLibrary = exportElement.exportedLibrary; | |
| if (exportedLibrary == null) { | |
| continue; | |
| } | |
| var exportedUri = exportedLibrary.uri; | |
| var exportedBuilder = linker.builders[exportedUri]; | |
| var combinators = exportElement.combinators.build(); | |
| var export = Export( | |
| exporter: this, | |
| location: ExportLocation( | |
| fragmentIndex: fragmentIndex, | |
| exportIndex: exportIndex, | |
| ), | |
| combinators: combinators, | |
| ); | |
| if (exportedBuilder != null) { | |
| exportedBuilder.exports.add(export); | |
| } else { | |
| var exportEntries = exportedLibrary.exportEntries; | |
| for (var entry in exportEntries) { | |
| export.addToExportScope(entry); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void addTopFragment(LibraryFragmentImpl parent, FragmentImpl fragment) { | |
| fragment.enclosingFragment = parent; | |
| (_topFragments[parent] ??= []).add(fragment); | |
| } | |
| void buildClassSyntheticConstructors() { | |
| for (var classElement in element.children) { | |
| if (classElement is! ClassElementImpl) continue; | |
| if (classElement.isMixinApplication) continue; | |
| if (classElement.constructors.isNotEmpty) continue; | |
| var fragment = ConstructorFragmentImpl(name: 'new'); | |
| fragment.isOriginImplicitDefault = true; | |
| fragment.typeName = classElement.name; | |
| classElement.firstFragment.constructors = [fragment].toFixedList(); | |
| classElement.constructors = [ | |
| ConstructorElementImpl( | |
| name: fragment.name, | |
| reference: classElement.reference.declareConstructor('new'), | |
| firstFragment: fragment, | |
| ), | |
| ]; | |
| } | |
| } | |
| /// Build elements for declarations in the library units, add top-level | |
| /// declarations to the local scope, for combining into export scopes. | |
| void buildElements() { | |
| _buildDirectives(kind: kind, containerUnit: element.firstFragment); | |
| for (var linkingUnit in units) { | |
| var fragmentBuilder = FragmentBuilder( | |
| libraryBuilder: this, | |
| libraryFragment: linkingUnit.fragment, | |
| ); | |
| fragmentBuilder.buildDirectives(linkingUnit.node); | |
| fragmentBuilder.buildDeclarationFragments(linkingUnit.node); | |
| if (linkingUnit is DefiningLinkingUnit) { | |
| fragmentBuilder.buildLibraryMetadata(linkingUnit.node); | |
| } | |
| } | |
| ElementBuilder(libraryBuilder: this).buildElements( | |
| topFragments: _topFragments, | |
| parentChildFragments: _parentChildFragments, | |
| ); | |
| for (var linkingUnit in units) { | |
| InformativeDataApplier().applyFromBytes( | |
| linkingUnit.fragment, | |
| linkingUnit.informativeBytes, | |
| ); | |
| } | |
| _declareDartCoreDynamicNever(); | |
| } | |
| void buildEnumChildren() { | |
| var typeProvider = element.typeProvider; | |
| for (var enum_ in implicitEnumNodes.values) { | |
| enum_.fragment.element.supertype = | |
| typeProvider.enumType ?? typeProvider.objectType; | |
| var valuesType = typeProvider.listType( | |
| element.typeSystem.instantiateInterfaceToBounds( | |
| element: enum_.fragment.element, | |
| nullabilitySuffix: typeProvider.objectType.nullabilitySuffix, | |
| ), | |
| ); | |
| enum_.valuesTypeNode.type = valuesType; | |
| enum_.valuesFragment.element.type = valuesType; | |
| } | |
| } | |
| void buildEnumSyntheticConstructors() { | |
| bool hasConstructor(EnumElementImpl enumElement) { | |
| for (var constructor in enumElement.constructors) { | |
| if (constructor.isGenerative || constructor.name == 'new') { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| for (var enumElement in element.enums) { | |
| if (hasConstructor(enumElement)) continue; | |
| var constructorFragment = ConstructorFragmentImpl(name: 'new') | |
| ..isOriginImplicitDefault = true | |
| ..isConst = true | |
| ..typeName = enumElement.name; | |
| enumElement.firstFragment.addConstructor(constructorFragment); | |
| var constructorElement = ConstructorElementImpl( | |
| name: constructorFragment.name, | |
| reference: enumElement.reference.declareConstructor('new'), | |
| firstFragment: constructorFragment, | |
| ); | |
| enumElement.addConstructor(constructorElement); | |
| } | |
| } | |
| void buildInitialExportScope() { | |
| exportScope = ExportScope(); | |
| _declaredReferences.forEach((name, reference) { | |
| if (name.startsWith('_')) return; | |
| exportScope.declare(name, reference); | |
| }); | |
| } | |
| void collectMixinSuperInvokedNames() { | |
| for (var linkingUnit in units) { | |
| for (var declaration in linkingUnit.node.declarations2) { | |
| if (declaration is ast.MixinDeclarationImpl) { | |
| var names = <String>{}; | |
| var collector = MixinSuperInvokedNamesCollector(names); | |
| for (var executable in declaration.body.members) { | |
| if (executable is ast.MethodDeclarationImpl) { | |
| executable.body.accept2(collector); | |
| } | |
| } | |
| var fragment = declaration.declaredFragment!; | |
| fragment.superInvokedNames = names.sorted(); | |
| } | |
| } | |
| } | |
| } | |
| /// Computes which fields in this library are promotable. | |
| void computeFieldPromotability() { | |
| _FieldPromotability( | |
| this, | |
| enabled: element.featureSet.isEnabled(Feature.inference_update_2), | |
| ).perform(); | |
| } | |
| void declare(Element element, ExportableReference reference) { | |
| if (element.lookupName case var lookupName?) { | |
| _declaredReferences[lookupName] = reference; | |
| } | |
| } | |
| void replaceConstFieldsIfNoConstConstructor() { | |
| var hasConstConstructorCache = <InterfaceElement, bool>{}; | |
| bool hasConstConstructor(Element element) { | |
| if (element is InterfaceElement) { | |
| var result = hasConstConstructorCache[element]; | |
| if (result == null) { | |
| result = element.constructors.any((e) => e.isConst); | |
| hasConstConstructorCache[element] = result; | |
| } | |
| return result; | |
| } | |
| return false; | |
| } | |
| for (var fieldFragment in finalInstanceFields) { | |
| var enclosingElement = fieldFragment.enclosingFragment.element; | |
| if (!hasConstConstructor(enclosingElement)) { | |
| fieldFragment.constantInitializer2 = null; | |
| } | |
| } | |
| } | |
| void resolveConstructorFieldFormals() { | |
| for (var interfaceElement in element.children) { | |
| if (interfaceElement is! InterfaceElementImpl) { | |
| continue; | |
| } | |
| if (interfaceElement is ClassElementImpl && | |
| interfaceElement.isMixinApplication) { | |
| continue; | |
| } | |
| for (var constructor in interfaceElement.constructors) { | |
| for (var parameter in constructor.formalParametersIncludingRecovery) { | |
| if (parameter is FieldFormalParameterElementImpl) { | |
| parameter.field = interfaceElement.getField( | |
| parameter.privateName ?? parameter.name ?? '', | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void resolveConstructors() { | |
| ConstructorInitializerResolver(linker, this).resolve(); | |
| } | |
| void resolveDefaultValues() { | |
| DefaultValueResolver(linker, this).resolve(); | |
| } | |
| void resolveMetadata() { | |
| for (var linkingUnit in units) { | |
| var resolver = MetadataResolver(linker, linkingUnit.fragment, this); | |
| linkingUnit.node.accept2(resolver); | |
| } | |
| } | |
| void resolveTypes(NodesToBuildType nodesToBuildType) { | |
| for (var linkingUnit in units) { | |
| var resolver = ReferenceResolver( | |
| linker, | |
| nodesToBuildType, | |
| element.typeSystem, | |
| linkingUnit.fragment.scope, | |
| libraryFragment: linkingUnit.fragment, | |
| ); | |
| linkingUnit.node.accept2(resolver); | |
| } | |
| } | |
| void storeExportScope() { | |
| element.exportEntries = exportScope.toExportEntries(); | |
| var definedNames = <String, Element>{}; | |
| for (var entry in exportScope.entriesByName.values) { | |
| var element = linker.elementFactory.elementOfReference3(entry.reference); | |
| definedNames[entry.name] = element; | |
| } | |
| var namespace = Namespace(definedNames); | |
| element.exportNamespace = namespace; | |
| var entryPoint = namespace.get2(TopLevelFunctionElement.MAIN_FUNCTION_NAME); | |
| if (entryPoint is TopLevelFunctionElementImpl) { | |
| element.entryPoint = entryPoint; | |
| } | |
| } | |
| /// Builds directive elements, for the library and recursively for its | |
| /// augmentations. | |
| void _buildDirectives({ | |
| required FileKind kind, | |
| required LibraryFragmentImpl containerUnit, | |
| }) { | |
| containerUnit.libraryExports = kind.libraryExports.map((state) { | |
| return _buildLibraryExport(state); | |
| }).toFixedList(); | |
| containerUnit.libraryImports = kind.libraryImports.map((state) { | |
| return _buildLibraryImport(containerUnit: containerUnit, state: state); | |
| }).toFixedList(); | |
| containerUnit.parts = kind.partIncludes.map((partState) { | |
| return _buildPartInclude( | |
| containerLibrary: element, | |
| containerUnit: containerUnit, | |
| state: partState, | |
| ); | |
| }).toFixedList(); | |
| } | |
| LibraryExportImpl _buildLibraryExport(LibraryExportState state) { | |
| DirectiveUri uri; | |
| switch (state) { | |
| case LibraryExportWithFile(): | |
| var exportedLibraryKind = state.exportedLibrary; | |
| if (exportedLibraryKind != null) { | |
| var exportedFile = exportedLibraryKind.file; | |
| var exportedUri = exportedFile.uri; | |
| var elementFactory = linker.elementFactory; | |
| var exportedLibrary = elementFactory.libraryOfUri2(exportedUri); | |
| uri = DirectiveUriWithLibraryImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: exportedLibrary.source, | |
| library: exportedLibrary, | |
| ); | |
| } else { | |
| uri = DirectiveUriWithSourceImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: state.exportedSource, | |
| ); | |
| } | |
| case LibraryExportWithInSummarySource(): | |
| var exportedLibrarySource = state.exportedLibrarySource; | |
| if (exportedLibrarySource != null) { | |
| var exportedUri = exportedLibrarySource.uri; | |
| var elementFactory = linker.elementFactory; | |
| var exportedLibrary = elementFactory.libraryOfUri2(exportedUri); | |
| uri = DirectiveUriWithLibraryImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: exportedLibrary.source, | |
| library: exportedLibrary, | |
| ); | |
| } else { | |
| uri = DirectiveUriWithSourceImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: state.exportedSource, | |
| ); | |
| } | |
| default: | |
| var selectedUri = state.selectedUri; | |
| switch (selectedUri) { | |
| case file_state.DirectiveUriWithUri(): | |
| uri = DirectiveUriWithRelativeUriImpl( | |
| relativeUriString: selectedUri.relativeUriStr, | |
| relativeUri: selectedUri.relativeUri, | |
| ); | |
| case file_state.DirectiveUriWithString(): | |
| uri = DirectiveUriWithRelativeUriStringImpl( | |
| relativeUriString: selectedUri.relativeUriStr, | |
| ); | |
| default: | |
| uri = DirectiveUriImpl(); | |
| } | |
| } | |
| return LibraryExportImpl( | |
| combinators: buildNamespaceCombinators(state.unlinked.combinators), | |
| exportKeywordOffset: state.unlinked.exportKeywordOffset, | |
| uri: uri, | |
| ); | |
| } | |
| LibraryImportImpl _buildLibraryImport({ | |
| required LibraryFragmentImpl containerUnit, | |
| required LibraryImportState state, | |
| }) { | |
| var prefixFragment = state.unlinked.prefix.mapOrNull((unlinked) { | |
| return _buildLibraryImportPrefixFragment( | |
| libraryFragment: containerUnit, | |
| unlinkedName: unlinked.name, | |
| offset: unlinked.nameOffset, | |
| isDeferred: unlinked.deferredOffset != null, | |
| ); | |
| }); | |
| return LibraryImportImpl( | |
| isSynthetic: state.isSyntheticDartCore, | |
| combinators: buildNamespaceCombinators(state.unlinked.combinators), | |
| importKeywordOffset: state.unlinked.importKeywordOffset, | |
| prefix: prefixFragment, | |
| uri: buildLibraryImportUri( | |
| state: state, | |
| elementFactory: linker.elementFactory, | |
| ), | |
| ); | |
| } | |
| PrefixFragmentImpl _buildLibraryImportPrefixFragment({ | |
| required LibraryFragmentImpl libraryFragment, | |
| required UnlinkedLibraryImportPrefixName? unlinkedName, | |
| required int offset, | |
| required bool isDeferred, | |
| }) { | |
| var fragment = PrefixFragmentImpl( | |
| name: unlinkedName?.name, | |
| firstTokenOffset: null, | |
| nameOffset: unlinkedName?.nameOffset, | |
| isDeferred: isDeferred, | |
| )..offset = offset; | |
| libraryFragment.bindLibraryImportPrefixElement( | |
| id: unlinkedName?.name ?? '#${_nextLocalReferenceId++}', | |
| fragment: fragment, | |
| ); | |
| return fragment; | |
| } | |
| PartIncludeImpl _buildPartInclude({ | |
| required LibraryElementImpl containerLibrary, | |
| required LibraryFragmentImpl containerUnit, | |
| required file_state.PartIncludeState state, | |
| }) { | |
| DirectiveUriImpl directiveUri; | |
| switch (state) { | |
| case PartIncludeWithFile(): | |
| var includedPart = state.includedPart; | |
| if (includedPart != null && _includedFiles.add(includedPart.file)) { | |
| var partFile = includedPart.file; | |
| var partUnitNode = partFile.parse( | |
| performance: OperationPerformanceImpl('<root>'), | |
| scanComments: false, | |
| ); | |
| var libraryFragment = LibraryFragmentImpl( | |
| library: containerLibrary, | |
| source: partFile.source, | |
| lineInfo: partUnitNode.lineInfo, | |
| ); | |
| partUnitNode.declaredFragment = libraryFragment; | |
| libraryFragment.isOriginNotExistingFile = !partFile.exists; | |
| libraryFragment.setCodeRange(0, partUnitNode.length); | |
| units.add( | |
| LinkingUnit( | |
| node: partUnitNode, | |
| fragment: libraryFragment, | |
| informativeBytes: partFile.unlinked2.informativeBytes, | |
| ), | |
| ); | |
| _buildDirectives(kind: includedPart, containerUnit: libraryFragment); | |
| directiveUri = DirectiveUriWithUnitImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| libraryFragment: libraryFragment, | |
| ); | |
| } else { | |
| directiveUri = DirectiveUriWithSourceImpl( | |
| relativeUriString: state.selectedUri.relativeUriStr, | |
| relativeUri: state.selectedUri.relativeUri, | |
| source: state.includedFile.source, | |
| ); | |
| } | |
| default: | |
| var uriState = state.selectedUri; | |
| switch (uriState) { | |
| case file_state.DirectiveUriWithSource(): | |
| directiveUri = DirectiveUriWithSourceImpl( | |
| relativeUriString: uriState.relativeUriStr, | |
| relativeUri: uriState.relativeUri, | |
| source: uriState.source, | |
| ); | |
| case file_state.DirectiveUriWithUri(): | |
| directiveUri = DirectiveUriWithRelativeUriImpl( | |
| relativeUriString: uriState.relativeUriStr, | |
| relativeUri: uriState.relativeUri, | |
| ); | |
| case file_state.DirectiveUriWithString(): | |
| directiveUri = DirectiveUriWithRelativeUriStringImpl( | |
| relativeUriString: uriState.relativeUriStr, | |
| ); | |
| default: | |
| directiveUri = DirectiveUriImpl(); | |
| } | |
| } | |
| return PartIncludeImpl( | |
| partKeywordOffset: state.unlinked.partKeywordOffset, | |
| uri: directiveUri, | |
| ); | |
| } | |
| /// We want to have stable references for `loadLibrary` function. But we | |
| /// cannot create the function itself right now, because the type provider | |
| /// might be not available yet. So, we reserve the reference right now, | |
| /// before any other declarations, and create the fragment and element later. | |
| void _createLoadLibraryReference() { | |
| element.loadLibraryProvider = LoadLibraryFunctionProvider( | |
| elementReference: references.declareTopLevelFunction( | |
| TopLevelFunctionElement.LOAD_LIBRARY_NAME, | |
| ), | |
| ); | |
| } | |
| /// These elements are implicitly declared in `dart:core`. | |
| void _declareDartCoreDynamicNever() { | |
| if (reference.uriString == 'dart:core') { | |
| reference.dynamicRef.element = DynamicElementImpl.instance; | |
| declare(DynamicElementImpl.instance, reference.dynamicRef); | |
| reference.neverRef.element = NeverElementImpl.instance; | |
| declare(NeverElementImpl.instance, reference.neverRef); | |
| } | |
| } | |
| static void build({ | |
| required Linker linker, | |
| required LibraryFileKind inputLibrary, | |
| required OperationPerformanceImpl performance, | |
| }) { | |
| var elementFactory = linker.elementFactory; | |
| var rootReference = linker.rootReference; | |
| var libraryFile = inputLibrary.file; | |
| var libraryReference = rootReference.getOrCreateLibrary(libraryFile.uri); | |
| var libraryUnitNode = performance.run('libraryFile', (performance) { | |
| return libraryFile.parse(performance: performance, scanComments: false); | |
| }); | |
| var name = ''; | |
| var nameOffset = -1; | |
| var nameLength = 0; | |
| for (var directive in libraryUnitNode.directives) { | |
| if (directive is ast.LibraryDirectiveImpl) { | |
| var nameIdentifier = directive.name; | |
| if (nameIdentifier != null) { | |
| name = nameIdentifier.tokens.map((e) => e.lexeme).join(); | |
| nameOffset = nameIdentifier.offset; | |
| nameLength = nameIdentifier.length; | |
| } | |
| break; | |
| } | |
| } | |
| var libraryElement = LibraryElementImpl( | |
| elementFactory.analysisContext, | |
| elementFactory.analysisSession, | |
| name, | |
| nameOffset, | |
| nameLength, | |
| libraryUnitNode.featureSet, | |
| ); | |
| if (!libraryFile.exists) { | |
| libraryElement.isSynthetic = true; | |
| } | |
| libraryElement.languageVersion = libraryUnitNode.languageVersion; | |
| libraryElement.reference = libraryReference; | |
| libraryReference.element = libraryElement; | |
| var linkingUnits = <LinkingUnit>[]; | |
| { | |
| var libraryFragment = LibraryFragmentImpl( | |
| library: libraryElement, | |
| source: libraryFile.source, | |
| lineInfo: libraryUnitNode.lineInfo, | |
| ); | |
| libraryUnitNode.declaredFragment = libraryFragment; | |
| libraryFragment.isOriginNotExistingFile = !libraryFile.exists; | |
| libraryFragment.setCodeRange(0, libraryUnitNode.length); | |
| linkingUnits.add( | |
| DefiningLinkingUnit( | |
| node: libraryUnitNode, | |
| fragment: libraryFragment, | |
| informativeBytes: libraryFile.unlinked2.informativeBytes, | |
| ), | |
| ); | |
| libraryElement.firstFragment = libraryFragment; | |
| } | |
| var builder = LibraryBuilder._( | |
| linker: linker, | |
| kind: inputLibrary, | |
| uri: libraryFile.uri, | |
| reference: libraryReference, | |
| element: libraryElement, | |
| units: linkingUnits, | |
| ); | |
| builder._createLoadLibraryReference(); | |
| elementFactory.setLibraryTypeSystem(libraryElement); | |
| linker.builders[builder.uri] = builder; | |
| } | |
| } | |
| class LinkingUnit { | |
| final ast.CompilationUnitImpl node; | |
| final LibraryFragmentImpl fragment; | |
| final Uint8List informativeBytes; | |
| LinkingUnit({ | |
| required this.node, | |
| required this.fragment, | |
| required this.informativeBytes, | |
| }); | |
| } | |
| /// This class examines all the [InterfaceElementImpl]s in a library and | |
| /// determines which fields are promotable within that library. | |
| class _FieldPromotability | |
| extends | |
| FieldPromotability< | |
| InterfaceElementImpl, | |
| FieldElementImpl, | |
| GetterElementImpl | |
| > { | |
| /// The [_libraryBuilder] for the library being analyzed. | |
| final LibraryBuilder _libraryBuilder; | |
| final bool enabled; | |
| /// Fields that might be promotable, if not marked unpromotable later. | |
| final List<FieldElementImpl> _potentiallyPromotableFields = []; | |
| _FieldPromotability(this._libraryBuilder, {required this.enabled}); | |
| @override | |
| Iterable<InterfaceElementImpl> getSuperclasses( | |
| InterfaceElementImpl class_, { | |
| required bool ignoreImplements, | |
| }) { | |
| var result = <InterfaceElementImpl>[]; | |
| var supertype = class_.supertype; | |
| if (supertype != null) { | |
| result.add(supertype.element); | |
| } | |
| for (var mixin in class_.mixins) { | |
| result.add(mixin.element); | |
| } | |
| if (!ignoreImplements) { | |
| for (var interface in class_.interfaces) { | |
| result.add(interface.element); | |
| } | |
| if (class_ is MixinElementImpl) { | |
| for (var constraint in class_.superclassConstraints) { | |
| result.add(constraint.element); | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| /// Computes which fields are promotable and updates their `isPromotable` | |
| /// properties accordingly. | |
| void perform() { | |
| // Iterate through all the classes, enums, and mixins in the library, | |
| // recording the non-synthetic instance fields and getters of each. | |
| var element = _libraryBuilder.element; | |
| for (var class_ in element.classes) { | |
| _handleMembers(addClass(class_, isAbstract: class_.isAbstract), class_); | |
| } | |
| for (var enum_ in element.enums) { | |
| _handleMembers(addClass(enum_, isAbstract: false), enum_); | |
| } | |
| for (var mixin_ in element.mixins) { | |
| _handleMembers(addClass(mixin_, isAbstract: true), mixin_); | |
| } | |
| // Private representation fields of extension types are always promotable. | |
| // They also don't affect promotability of any other fields. | |
| for (var extensionType in element.extensionTypes) { | |
| var representation = extensionType.representation; | |
| var representationName = representation.name; | |
| if (representationName != null) { | |
| if (representationName.startsWith('_')) { | |
| representation.firstFragment.isPromotable = true; | |
| } | |
| } | |
| } | |
| // Compute the set of field names that are not promotable. | |
| var fieldNonPromotabilityInfo = computeNonPromotabilityInfo(); | |
| // Set the `isPromotable` bit for each field element that *is* promotable. | |
| for (var field in _potentiallyPromotableFields) { | |
| if (fieldNonPromotabilityInfo[field.name!] == null) { | |
| field.firstFragment.isPromotable = true; | |
| } | |
| } | |
| element.fieldNameNonPromotabilityInfo = { | |
| for (var MapEntry(:key, :value) in fieldNonPromotabilityInfo.entries) | |
| key: element_model.FieldNameNonPromotabilityInfo( | |
| conflictingFields: value.conflictingFields, | |
| conflictingGetters: value.conflictingGetters, | |
| conflictingNsmClasses: value.conflictingNsmClasses, | |
| ), | |
| }; | |
| } | |
| /// Records all the non-synthetic instance fields and getters of [class_] | |
| /// into [classInfo]. | |
| void _handleMembers( | |
| ClassInfo<InterfaceElementImpl> classInfo, | |
| InterfaceElementImpl class_, | |
| ) { | |
| for (var field in class_.fields) { | |
| if (field.isStatic || field.isOriginGetterSetter) { | |
| continue; | |
| } | |
| var fieldName = field.name; | |
| if (fieldName != null) { | |
| var nonPromotabilityReason = addField( | |
| classInfo, | |
| field, | |
| fieldName, | |
| isFinal: field.isFinal, | |
| isAbstract: field.isAbstract, | |
| isExternal: field.isExternal, | |
| ); | |
| if (enabled && nonPromotabilityReason == null) { | |
| _potentiallyPromotableFields.add(field); | |
| } | |
| } | |
| } | |
| for (var getter in class_.getters) { | |
| if (getter.isStatic || getter.isOriginVariable) { | |
| continue; | |
| } | |
| var getterName = getter.name; | |
| if (getterName != null) { | |
| var nonPromotabilityReason = addGetter( | |
| classInfo, | |
| getter, | |
| getterName, | |
| isAbstract: getter.isAbstract, | |
| ); | |
| if (enabled && nonPromotabilityReason == null) { | |
| var field = getter.variable as FieldElementImpl; | |
| _potentiallyPromotableFields.add(field); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| extension<T> on T? { | |
| R? mapOrNull<R>(R Function(T) mapper) { | |
| var self = this; | |
| return self != null ? mapper(self) : null; | |
| } | |
| } |