blob: 7adbf575dda24976f855124f4b7e0cdcb184794c [file] [log] [blame]
// 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 'package:_fe_analyzer_shared/src/field_promotability.dart';
import 'package:_fe_analyzer_shared/src/macros/code_optimizer.dart' as macro;
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/source_range.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/info_declaration_store.dart';
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/dart/scanner/scanner.dart';
import 'package:analyzer/src/summary2/augmentation.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/macro_application.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/macro_merge.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:analyzer/src/utilities/extensions/object.dart';
import 'package:macros/src/executor.dart' as macro;
class DefiningLinkingUnit extends LinkingUnit {
DefiningLinkingUnit({
required super.reference,
required super.node,
required super.element,
required super.container,
});
}
class ImplicitEnumNodes {
final EnumElementImpl element;
final ast.NamedTypeImpl valuesTypeNode;
final ast.VariableDeclarationImpl valuesNode;
final ConstFieldElementImpl valuesElement;
final Set<String> valuesNames;
ast.ListLiteralImpl valuesInitializer;
ImplicitEnumNodes({
required this.element,
required this.valuesTypeNode,
required this.valuesNode,
required this.valuesElement,
required this.valuesNames,
required this.valuesInitializer,
});
}
class LibraryBuilder with MacroApplicationsContainer {
static const _enableMacroCodeOptimizer = false;
final Linker linker;
final LibraryFileKind kind;
final Uri uri;
final Reference reference;
final LibraryElementImpl element;
final List<LinkingUnit> units;
final Map<EnumElementImpl, ImplicitEnumNodes> implicitEnumNodes =
Map.identity();
/// The top-level elements that can be augmented.
final Map<String, AugmentedInstanceDeclarationBuilder> _augmentedBuilders =
{};
/// The top-level variables and accessors that can be augmented.
late final AugmentedTopVariablesBuilder topVariables =
AugmentedTopVariablesBuilder(_augmentationTargets);
/// The top-level elements that can be augmented.
final Map<String, ElementImpl> _augmentationTargets = {};
/// Local declarations.
final Map<String, Reference> _declaredReferences = {};
/// The export scope of the library.
ExportScope exportScope = ExportScope();
/// The `export` directives that export this library.
final List<Export> exports = [];
/// The fields that were speculatively created as [ConstFieldElementImpl],
/// but we want to clear [ConstVariableElement.constantInitializer] 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, user
/// written or macro generated.
final Set<ConstFieldElementImpl> finalInstanceFields = Set.identity();
final List<List<macro.MacroExecutionResult>> _macroResults = [];
LibraryBuilder._({
required this.linker,
required this.kind,
required this.uri,
required this.reference,
required this.element,
required this.units,
});
void addExporters() {
var containers = [element, ...element.augmentations];
for (var containerIndex = 0;
containerIndex < containers.length;
containerIndex++) {
var container = containers[containerIndex];
var exportElements = container.libraryExports;
for (var exportIndex = 0;
exportIndex < exportElements.length;
exportIndex++) {
var exportElement = exportElements[exportIndex];
var exportedLibrary = exportElement.exportedLibrary;
if (exportedLibrary is! LibraryElementImpl) {
continue;
}
var combinators = exportElement.combinators.map((combinator) {
if (combinator is ShowElementCombinator) {
return Combinator.show(combinator.shownNames);
} else if (combinator is HideElementCombinator) {
return Combinator.hide(combinator.hiddenNames);
} else {
throw UnimplementedError();
}
}).toList();
var exportedUri = exportedLibrary.source.uri;
var exportedBuilder = linker.builders[exportedUri];
var export = Export(
exporter: this,
location: ExportLocation(
containerIndex: containerIndex,
exportIndex: exportIndex,
),
combinators: combinators,
);
if (exportedBuilder != null) {
exportedBuilder.exports.add(export);
} else {
var exportedReferences = exportedLibrary.exportedReferences;
for (var exported in exportedReferences) {
var reference = exported.reference;
var name = reference.name;
if (reference.isSetter) {
export.addToExportScope('$name=', exported);
} else {
export.addToExportScope(name, exported);
}
}
}
}
}
}
void buildClassSyntheticConstructors() {
bool hasConstructor(ClassElementImpl element) {
if (element.constructors.isNotEmpty) return true;
if (element.augmentation case var augmentation?) {
return hasConstructor(augmentation);
}
return false;
}
for (var classElement in element.topLevelElements) {
if (classElement is! ClassElementImpl) continue;
if (classElement.isMixinApplication) continue;
if (classElement.augmentationTarget != null) continue;
if (hasConstructor(classElement)) continue;
var constructor = ConstructorElementImpl('', -1)..isSynthetic = true;
var containerRef = classElement.reference!.getChild('@constructor');
var reference = containerRef.getChild('new');
reference.element = constructor;
constructor.reference = reference;
classElement.constructors = [constructor].toFixedList();
if (classElement.augmented case AugmentedClassElementImpl augmented) {
augmented.constructors = classElement.constructors;
}
}
}
/// 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,
container: element,
);
for (var linkingUnit in units) {
var elementBuilder = ElementBuilder(
libraryBuilder: this,
container: linkingUnit.container,
unitReference: linkingUnit.reference,
unitElement: linkingUnit.element,
);
if (linkingUnit is DefiningLinkingUnit) {
elementBuilder.buildLibraryElementChildren(linkingUnit.node);
}
elementBuilder.buildDeclarationElements(linkingUnit.node);
}
_declareDartCoreDynamicNever();
}
void buildEnumChildren() {
var typeProvider = element.typeProvider;
for (var enum_ in implicitEnumNodes.values) {
enum_.element.supertype =
typeProvider.enumType ?? typeProvider.objectType;
var valuesType = typeProvider.listType(
element.typeSystem.instantiateInterfaceToBounds(
element: enum_.element,
nullabilitySuffix: typeProvider.objectType.nullabilitySuffix,
),
);
enum_.valuesTypeNode.type = valuesType;
enum_.valuesElement.type = valuesType;
}
}
void buildEnumSyntheticConstructors() {
bool hasConstructor(EnumElementImpl element) {
for (var constructor in element.augmented.constructors) {
if (constructor.isGenerative || constructor.name == '') {
return true;
}
}
return false;
}
for (var enumElement in element.topLevelElements) {
if (enumElement is! EnumElementImpl) continue;
if (enumElement.augmentationTarget != null) continue;
if (hasConstructor(enumElement)) continue;
var constructor = ConstructorElementImpl('', -1)
..isConst = true
..isSynthetic = true;
var containerRef = enumElement.reference!.getChild('@constructor');
var reference = containerRef.getChild('new');
reference.element = constructor;
constructor.reference = reference;
enumElement.constructors = [
...enumElement.constructors,
constructor,
].toFixedList();
if (enumElement.augmented case AugmentedEnumElementImpl augmented) {
augmented.constructors = enumElement.constructors;
}
}
}
void buildInitialExportScope() {
exportScope = ExportScope();
_declaredReferences.forEach((name, reference) {
if (name.startsWith('_')) return;
if (reference.isPrefix) return;
exportScope.declare(name, reference);
});
}
void collectMixinSuperInvokedNames() {
for (var linkingUnit in units) {
for (var declaration in linkingUnit.node.declarations) {
if (declaration is ast.MixinDeclarationImpl) {
var names = <String>{};
var collector = MixinSuperInvokedNamesCollector(names);
for (var executable in declaration.members) {
if (executable is ast.MethodDeclarationImpl) {
executable.body.accept(collector);
}
}
var element = declaration.declaredElement as MixinElementImpl;
element.superInvokedNames = names.toList();
}
}
}
}
/// Computes which fields in this library are promotable.
void computeFieldPromotability() {
_FieldPromotability(this,
enabled: element.featureSet.isEnabled(Feature.inference_update_2))
.perform();
}
void declare(String name, Reference reference) {
_declaredReferences[name] = reference;
}
void disposeMacroApplications() {
var macroApplier = linker.macroApplier;
if (macroApplier == null) {
return;
}
macroApplier.disposeMacroApplications(
libraryBuilder: this,
);
}
/// Completes with `true` if a macro application was run in this library.
///
/// Completes with `false` if there are no macro applications to run, either
/// because we ran all, or those that we have not run yet have dependencies
/// of interfaces declared in other libraries that, and we have not run yet
/// declarations phase macro applications for them.
Future<MacroDeclarationsPhaseStepResult> executeMacroDeclarationsPhase({
required ElementImpl? targetElement,
required OperationPerformanceImpl performance,
}) async {
var macroApplier = linker.macroApplier;
if (macroApplier == null) {
return MacroDeclarationsPhaseStepResult.nothing;
}
var applicationResult = await macroApplier.executeDeclarationsPhase(
libraryBuilder: this,
targetElement: targetElement,
performance: performance,
);
// No more applications to execute.
if (applicationResult == null) {
return MacroDeclarationsPhaseStepResult.nothing;
}
await _addMacroResults(
macroApplier,
applicationResult,
phase: macro.Phase.declarations,
);
// Check if a new top-level declaration was added.
var augmentationUnit = units.last.element;
if (augmentationUnit.functions.isNotEmpty ||
augmentationUnit.topLevelVariables.isNotEmpty) {
element.resetScope();
return MacroDeclarationsPhaseStepResult.topDeclaration;
}
// Probably class member declarations.
return MacroDeclarationsPhaseStepResult.otherProgress;
}
Future<void> executeMacroDefinitionsPhase({
required OperationPerformanceImpl performance,
}) async {
var macroApplier = linker.macroApplier;
if (macroApplier == null) {
return;
}
while (true) {
var applicationResult = await performance.runAsync(
'executeDefinitionsPhase',
(performance) async {
return await macroApplier.executeDefinitionsPhase(
libraryBuilder: this,
performance: performance,
);
},
);
// No more applications to execute.
if (applicationResult == null) {
return;
}
await performance.runAsync(
'addMacroResults',
(performance) async {
await _addMacroResults(
macroApplier,
applicationResult,
phase: macro.Phase.definitions,
);
},
);
}
}
Future<void> executeMacroTypesPhase({
required OperationPerformanceImpl performance,
}) async {
var macroApplier = linker.macroApplier;
if (macroApplier == null) {
return;
}
while (true) {
var applicationResult = await macroApplier.executeTypesPhase(
libraryBuilder: this,
);
// No more applications to execute.
if (applicationResult == null) {
break;
}
await _addMacroResults(
macroApplier,
applicationResult,
phase: macro.Phase.types,
);
}
}
/// Fills with macro applications in user code.
Future<void> fillMacroApplier(LibraryMacroApplier macroApplier) async {
for (var linkingUnit in units) {
await macroApplier.add(
libraryBuilder: this,
container: element,
unit: linkingUnit.node,
);
}
}
AugmentedInstanceDeclarationBuilder? getAugmentedBuilder(String name) {
return _augmentedBuilders[name];
}
/// Merges accumulated [_macroResults] and corresponding macro augmentation
/// libraries into a single macro augmentation library.
Future<void> mergeMacroAugmentations({
required OperationPerformanceImpl performance,
}) async {
var macroApplier = linker.macroApplier;
if (macroApplier == null) {
return;
}
var augmentationCode = macroApplier.buildAugmentationLibraryCode(
uri,
_macroResults.flattenedToList2,
);
if (augmentationCode == null) {
return;
}
var mergedUnit = kind.file.parseCode(
code: augmentationCode,
errorListener: AnalysisErrorListener.NULL_LISTENER,
);
kind.disposeMacroAugmentations(disposeFiles: true);
// Remove import for partial macro augmentations.
element.augmentationImports = element.augmentationImports
.take(element.augmentationImports.length - _macroResults.length)
.toFixedList();
// Remove units with partial macro augmentations.
var partialUnits = units.sublist(units.length - _macroResults.length);
units.length -= _macroResults.length;
List<macro.Edit> optimizedCodeEdits;
String optimizedCode;
if (_enableMacroCodeOptimizer) {
optimizedCodeEdits = _CodeOptimizer(
elementFactory: linker.elementFactory,
).optimize(
augmentationCode,
libraryDeclarationNames: element.definingCompilationUnit.children
.map((e) => e.name)
.nonNulls
.toSet(),
scannerConfiguration: Scanner.buildConfig(kind.file.featureSet),
);
optimizedCode = macro.Edit.applyList(
optimizedCodeEdits,
augmentationCode,
);
} else {
optimizedCodeEdits = [];
optimizedCode = augmentationCode;
}
var importState = kind.addMacroAugmentation(
optimizedCode,
partialIndex: null,
);
var importedAugmentation = importState.importedAugmentation!;
var importedFile = importedAugmentation.file;
var unitNode = importedFile.parse();
var unitElement = CompilationUnitElementImpl(
source: importedFile.source,
librarySource: importedFile.source,
lineInfo: unitNode.lineInfo,
);
unitElement.setCodeRange(0, unitNode.length);
var unitReference =
reference.getChild('@augmentation').getChild(importedFile.uriStr);
_bindReference(unitReference, unitElement);
var augmentation = LibraryAugmentationElementImpl(
augmentationTarget: element,
nameOffset: importedAugmentation.unlinked.libraryKeywordOffset,
);
augmentation.definingCompilationUnit = unitElement;
augmentation.reference = unitReference;
var informativeBytes = importedFile.unlinked2.informativeBytes;
augmentation.macroGenerated = MacroGeneratedAugmentationLibrary(
code: importedFile.content,
informativeBytes: informativeBytes,
);
_buildDirectives(
kind: importedAugmentation,
container: augmentation,
);
MacroElementsMerger(
partialUnits: partialUnits,
unitReference: unitReference,
unitNode: unitNode,
unitElement: unitElement,
augmentation: augmentation,
).perform(updateConstants: () {
MacroUpdateConstantsForOptimizedCode(
libraryElement: element,
unitNode: mergedUnit,
codeEdits: optimizedCodeEdits,
unitElement: unitElement,
).perform();
});
// Set offsets the same way as when reading from summary.
InformativeDataApplier(
linker.elementFactory,
{},
NoOpInfoDeclarationStore(),
).applyToUnit(unitElement, informativeBytes);
var importUri = DirectiveUriWithAugmentationImpl(
relativeUriString: importState.uri.relativeUriStr,
relativeUri: importState.uri.relativeUri,
source: importedFile.source,
augmentation: augmentation,
);
var import = AugmentationImportElementImpl(
importKeywordOffset: importState.unlinked.importKeywordOffset,
uri: importUri,
);
import.isSynthetic = true;
element.augmentationImports = [
...element.augmentationImports,
import,
].toFixedList();
}
void putAugmentedBuilder(
String name,
AugmentedInstanceDeclarationBuilder element,
) {
_augmentedBuilders[name] = element;
}
void replaceConstFieldsIfNoConstConstructor() {
var withConstConstructors = Set<ClassElementImpl>.identity();
for (var classElement in element.topLevelElements) {
if (classElement is! ClassElementImpl) continue;
if (classElement.isMixinApplication) continue;
if (classElement.isAugmentation) continue;
var hasConst = classElement.augmented.constructors.any((e) => e.isConst);
if (hasConst) {
withConstConstructors.add(classElement);
}
}
for (var fieldElement in finalInstanceFields) {
var enclosing = fieldElement.enclosingElement;
var augmented = enclosing.ifTypeOrNull<ClassElementImpl>()?.augmented;
if (augmented == null) continue;
if (!withConstConstructors.contains(augmented.declaration)) {
fieldElement.constantInitializer = null;
}
}
}
void resolveConstructorFieldFormals() {
for (var interface in element.topLevelElements) {
if (interface is! InterfaceElementImpl) {
continue;
}
if (interface is ClassElementImpl && interface.isMixinApplication) {
continue;
}
var augmented = interface.augmented;
for (var constructor in interface.constructors) {
for (var parameter in constructor.parameters) {
if (parameter is FieldFormalParameterElementImpl) {
parameter.field = augmented.getField(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.element, this);
linkingUnit.node.accept(resolver);
}
}
void resolveTypes(NodesToBuildType nodesToBuildType) {
for (var linkingUnit in units) {
var resolver = ReferenceResolver(
linker,
nodesToBuildType,
linkingUnit.container,
);
linkingUnit.node.accept(resolver);
}
}
void setDefaultSupertypes() {
var shouldResetClassHierarchies = false;
var objectType = element.typeProvider.objectType;
for (var interface in element.topLevelElements) {
switch (interface) {
case ClassElementImpl():
if (interface.augmentationTarget != null) continue;
if (interface.isDartCoreObject) continue;
if (interface.supertype == null) {
shouldResetClassHierarchies = true;
interface.supertype = objectType;
}
case MixinElementImpl():
if (interface.augmentationTarget != null) continue;
var augmented = interface.augmented;
if (augmented.superclassConstraints.isEmpty) {
shouldResetClassHierarchies = true;
interface.superclassConstraints = [objectType];
if (augmented is AugmentedMixinElementImpl) {
augmented.superclassConstraints = [objectType];
}
}
}
}
if (shouldResetClassHierarchies) {
element.session.classHierarchy.removeOfLibraries({uri});
}
}
void storeExportScope() {
element.exportedReferences = exportScope.toReferences();
var definedNames = <String, Element>{};
for (var entry in exportScope.map.entries) {
var reference = entry.value.reference;
var element = linker.elementFactory.elementOfReference(reference);
if (element != null) {
definedNames[entry.key] = element;
}
}
var namespace = Namespace(definedNames);
element.exportNamespace = namespace;
var entryPoint = namespace.get(FunctionElement.MAIN_FUNCTION_NAME);
if (entryPoint is FunctionElement) {
element.entryPoint = entryPoint;
}
}
void updateAugmentationTarget<T extends ElementImpl>(
String name,
AugmentableElement<T> augmentation,
) {
if (augmentation.isAugmentation) {
var target = _augmentationTargets[name];
target ??= topVariables.accessors[name];
target ??= topVariables.accessors['$name='];
augmentation.augmentationTargetAny = target;
if (target case AugmentableElement<T> target) {
augmentation.isAugmentationChainStart = false;
target.augmentation = augmentation as T;
}
}
_augmentationTargets[name] = augmentation;
}
LibraryAugmentationElementImpl _addMacroAugmentation(
AugmentationImportWithFile state,
) {
var import = _buildAugmentationImport(element, state);
import.isSynthetic = true;
element.augmentationImports = [
...element.augmentationImports,
import,
].toFixedList();
var augmentation = import.importedAugmentation!;
augmentation.macroGenerated = MacroGeneratedAugmentationLibrary(
code: state.importedFile.content,
informativeBytes: state.importedFile.unlinked2.informativeBytes,
);
return augmentation;
}
/// Add results from the declarations or definitions phase.
Future<void> _addMacroResults(
LibraryMacroApplier macroApplier,
ApplicationResult applicationResult, {
required macro.Phase phase,
}) async {
// No results from the application.
var results = applicationResult.results;
if (results.isEmpty) {
return;
}
var augmentationCode = macroApplier.buildAugmentationLibraryCode(
uri,
results,
);
if (augmentationCode == null) {
return;
}
var importState = kind.addMacroAugmentation(
augmentationCode,
partialIndex: _macroResults.length,
);
var augmentation = _addMacroAugmentation(importState);
var macroLinkingUnit = units.last;
// If the generated code contains declarations that are not allowed at
// this phase, then add a diagnostic, and discard the code.
var notAllowed = findDeclarationsNotAllowedAtPhase(
unit: macroLinkingUnit.node,
phase: phase,
);
if (notAllowed.isNotEmpty) {
var application = applicationResult.application;
application.target.element.addMacroDiagnostic(
NotAllowedDeclarationDiagnostic(
annotationIndex: application.annotationIndex,
phase: phase,
code: augmentationCode,
nodeRanges: notAllowed
.map((node) => SourceRange(node.offset, node.length))
.toList(),
),
);
units.removeLast();
element.augmentationImports =
element.augmentationImports.withoutLast.toFixedList();
kind.removeLastMacroAugmentation();
return;
}
ElementBuilder(
libraryBuilder: this,
container: macroLinkingUnit.container,
unitReference: macroLinkingUnit.reference,
unitElement: macroLinkingUnit.element,
).buildDeclarationElements(macroLinkingUnit.node);
if (phase != macro.Phase.types) {
var nodesToBuildType = NodesToBuildType();
var resolver = ReferenceResolver(linker, nodesToBuildType, augmentation);
macroLinkingUnit.node.accept(resolver);
TypesBuilder(linker).build(nodesToBuildType);
}
_macroResults.add(results);
// Append applications from the partial augmentation.
await macroApplier.add(
libraryBuilder: this,
container: augmentation,
unit: macroLinkingUnit.node,
);
}
AugmentationImportElementImpl _buildAugmentationImport(
LibraryOrAugmentationElementImpl augmentationTarget,
AugmentationImportState state,
) {
DirectiveUri uri;
if (state is AugmentationImportWithFile) {
var importedAugmentation = state.importedAugmentation;
if (importedAugmentation != null) {
var importedFile = importedAugmentation.file;
var unitNode = importedFile.parse();
var unitElement = CompilationUnitElementImpl(
source: importedFile.source,
// TODO(scheglov): Remove this parameter.
librarySource: importedFile.source,
lineInfo: unitNode.lineInfo,
);
unitNode.declaredElement = unitElement;
unitElement.setCodeRange(0, unitNode.length);
var unitReference =
reference.getChild('@augmentation').getChild(importedFile.uriStr);
_bindReference(unitReference, unitElement);
var augmentation = LibraryAugmentationElementImpl(
augmentationTarget: augmentationTarget,
nameOffset: importedAugmentation.unlinked.augmentKeywordOffset,
);
augmentation.definingCompilationUnit = unitElement;
augmentation.reference = unitElement.reference!;
units.add(
DefiningLinkingUnit(
reference: unitReference,
node: unitNode,
element: unitElement,
container: augmentation,
),
);
_buildDirectives(
kind: importedAugmentation,
container: augmentation,
);
uri = DirectiveUriWithAugmentationImpl(
relativeUriString: state.uri.relativeUriStr,
relativeUri: state.uri.relativeUri,
source: importedFile.source,
augmentation: augmentation,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.uri.relativeUriStr,
relativeUri: state.uri.relativeUri,
source: state.importedSource,
);
}
} else {
var selectedUri = state.uri;
if (selectedUri is file_state.DirectiveUriWithUri) {
uri = DirectiveUriWithRelativeUriImpl(
relativeUriString: selectedUri.relativeUriStr,
relativeUri: selectedUri.relativeUri,
);
} else if (selectedUri is file_state.DirectiveUriWithString) {
uri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: selectedUri.relativeUriStr,
);
} else {
uri = DirectiveUriImpl();
}
}
return AugmentationImportElementImpl(
importKeywordOffset: state.unlinked.importKeywordOffset,
uri: uri,
);
}
List<NamespaceCombinator> _buildCombinators(
List<UnlinkedCombinator> combinators2,
) {
return combinators2.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();
}
/// Builds directive elements, for the library and recursively for its
/// augmentations.
void _buildDirectives({
required LibraryOrAugmentationFileKind kind,
required LibraryOrAugmentationElementImpl container,
}) {
container.libraryExports = kind.libraryExports.map((state) {
return _buildExport(state);
}).toFixedList();
container.libraryImports = kind.libraryImports.map((state) {
return _buildImport(
container: container,
state: state,
);
}).toFixedList();
container.augmentationImports = kind.augmentationImports.map((state) {
return _buildAugmentationImport(container, state);
}).toFixedList();
}
LibraryExportElementImpl _buildExport(LibraryExportState state) {
var combinators = _buildCombinators(
state.unlinked.combinators,
);
DirectiveUri uri;
if (state is 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,
);
}
} else if (state is 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,
);
}
} else {
var selectedUri = state.selectedUri;
if (selectedUri is file_state.DirectiveUriWithUri) {
uri = DirectiveUriWithRelativeUriImpl(
relativeUriString: selectedUri.relativeUriStr,
relativeUri: selectedUri.relativeUri,
);
} else if (selectedUri is file_state.DirectiveUriWithString) {
uri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: selectedUri.relativeUriStr,
);
} else {
uri = DirectiveUriImpl();
}
}
return LibraryExportElementImpl(
combinators: combinators,
exportKeywordOffset: state.unlinked.exportKeywordOffset,
uri: uri,
);
}
LibraryImportElementImpl _buildImport({
required LibraryOrAugmentationElementImpl container,
required LibraryImportState state,
}) {
var importPrefix = state.unlinked.prefix.mapOrNull((unlinked) {
var prefix = _buildPrefix(
name: unlinked.name,
nameOffset: unlinked.nameOffset,
container: container,
);
if (unlinked.deferredOffset != null) {
return DeferredImportElementPrefixImpl(
element: prefix,
);
} else {
return ImportElementPrefixImpl(
element: prefix,
);
}
});
var combinators = _buildCombinators(
state.unlinked.combinators,
);
DirectiveUri uri;
if (state is LibraryImportWithFile) {
var importedLibraryKind = state.importedLibrary;
if (importedLibraryKind != null) {
var importedFile = importedLibraryKind.file;
var importedUri = importedFile.uri;
var elementFactory = linker.elementFactory;
var importedLibrary = elementFactory.libraryOfUri2(importedUri);
uri = DirectiveUriWithLibraryImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: importedLibrary.source,
library: importedLibrary,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.importedSource,
);
}
} else if (state is LibraryImportWithInSummarySource) {
var importedLibrarySource = state.importedLibrarySource;
if (importedLibrarySource != null) {
var importedUri = importedLibrarySource.uri;
var elementFactory = linker.elementFactory;
var importedLibrary = elementFactory.libraryOfUri2(importedUri);
uri = DirectiveUriWithLibraryImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: importedLibrary.source,
library: importedLibrary,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.importedSource,
);
}
} else {
var selectedUri = state.selectedUri;
if (selectedUri is file_state.DirectiveUriWithUri) {
uri = DirectiveUriWithRelativeUriImpl(
relativeUriString: selectedUri.relativeUriStr,
relativeUri: selectedUri.relativeUri,
);
} else if (selectedUri is file_state.DirectiveUriWithString) {
uri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: selectedUri.relativeUriStr,
);
} else {
uri = DirectiveUriImpl();
}
}
return LibraryImportElementImpl(
combinators: combinators,
importKeywordOffset: state.unlinked.importKeywordOffset,
prefix: importPrefix,
uri: uri,
)..isSynthetic = state.isSyntheticDartCore;
}
PrefixElementImpl _buildPrefix({
required String name,
required int nameOffset,
required LibraryOrAugmentationElementImpl container,
}) {
// TODO(scheglov): Make reference required.
var containerRef = container.reference!;
var reference = containerRef.getChild('@prefix').getChild(name);
var existing = reference.element;
if (existing is PrefixElementImpl) {
return existing;
} else {
var result = PrefixElementImpl(
name,
nameOffset,
reference: reference,
);
container.encloseElement(result);
return result;
}
}
/// These elements are implicitly declared in `dart:core`.
void _declareDartCoreDynamicNever() {
if (reference.name == 'dart:core') {
var dynamicRef = reference.getChild('dynamic');
dynamicRef.element = DynamicElementImpl.instance;
declare('dynamic', dynamicRef);
var neverRef = reference.getChild('Never');
neverRef.element = NeverElementImpl.instance;
declare('Never', neverRef);
}
}
static void build(Linker linker, LibraryFileKind inputLibrary) {
var elementFactory = linker.elementFactory;
var rootReference = linker.rootReference;
var libraryFile = inputLibrary.file;
var libraryUriStr = libraryFile.uriStr;
var libraryReference = rootReference.getChild(libraryUriStr);
var libraryUnitNode = libraryFile.parse();
var name = '';
var nameOffset = -1;
var nameLength = 0;
for (var directive in libraryUnitNode.directives) {
if (directive is ast.LibraryDirectiveImpl) {
var nameIdentifier = directive.name2;
if (nameIdentifier != null) {
name = nameIdentifier.components.map((e) => e.name).join('.');
nameOffset = nameIdentifier.offset;
nameLength = nameIdentifier.length;
}
break;
}
}
var libraryElement = LibraryElementImpl(
elementFactory.analysisContext,
elementFactory.analysisSession,
name,
nameOffset,
nameLength,
libraryUnitNode.featureSet,
);
libraryElement.isSynthetic = !libraryFile.exists;
libraryElement.languageVersion = libraryUnitNode.languageVersion!;
_bindReference(libraryReference, libraryElement);
elementFactory.setLibraryTypeSystem(libraryElement);
var unitContainerRef = libraryReference.getChild('@unit');
var linkingUnits = <LinkingUnit>[];
{
var unitElement = CompilationUnitElementImpl(
source: libraryFile.source,
librarySource: libraryFile.source,
lineInfo: libraryUnitNode.lineInfo,
);
libraryUnitNode.declaredElement = unitElement;
unitElement.isSynthetic = !libraryFile.exists;
unitElement.setCodeRange(0, libraryUnitNode.length);
var unitReference = unitContainerRef.getChild(libraryFile.uriStr);
_bindReference(unitReference, unitElement);
linkingUnits.add(
DefiningLinkingUnit(
reference: unitReference,
node: libraryUnitNode,
element: unitElement,
container: libraryElement,
),
);
libraryElement.definingCompilationUnit = unitElement;
}
libraryElement.parts = inputLibrary.parts.map((partState) {
var uriState = partState.uri;
DirectiveUri directiveUri;
if (partState is PartWithFile) {
var includedPart = partState.includedPart;
if (includedPart != null) {
var partFile = includedPart.file;
var partUnitNode = partFile.parse();
var unitElement = CompilationUnitElementImpl(
source: partFile.source,
librarySource: libraryFile.source,
lineInfo: partUnitNode.lineInfo,
);
partUnitNode.declaredElement = unitElement;
unitElement.isSynthetic = !partFile.exists;
unitElement.uri = partFile.uriStr;
unitElement.setCodeRange(0, partUnitNode.length);
var unitReference = unitContainerRef.getChild(partFile.uriStr);
_bindReference(unitReference, unitElement);
linkingUnits.add(
LinkingUnit(
reference: unitReference,
node: partUnitNode,
container: libraryElement,
element: unitElement,
),
);
directiveUri = DirectiveUriWithUnitImpl(
relativeUriString: partState.uri.relativeUriStr,
relativeUri: partState.uri.relativeUri,
unit: unitElement,
);
} else {
directiveUri = DirectiveUriWithSourceImpl(
relativeUriString: partState.uri.relativeUriStr,
relativeUri: partState.uri.relativeUri,
source: partState.includedFile.source,
);
}
} else if (uriState is file_state.DirectiveUriWithSource) {
directiveUri = DirectiveUriWithSourceImpl(
relativeUriString: uriState.relativeUriStr,
relativeUri: uriState.relativeUri,
source: uriState.source,
);
} else if (uriState is file_state.DirectiveUriWithUri) {
directiveUri = DirectiveUriWithRelativeUriImpl(
relativeUriString: uriState.relativeUriStr,
relativeUri: uriState.relativeUri,
);
} else if (uriState is file_state.DirectiveUriWithString) {
directiveUri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: uriState.relativeUriStr,
);
} else {
directiveUri = DirectiveUriImpl();
}
return directiveUri;
}).map((directiveUri) {
return PartElementImpl(
uri: directiveUri,
);
}).toFixedList();
var builder = LibraryBuilder._(
linker: linker,
kind: inputLibrary,
uri: libraryFile.uri,
reference: libraryReference,
element: libraryElement,
units: linkingUnits,
);
linker.builders[builder.uri] = builder;
}
static void _bindReference(Reference reference, ElementImpl element) {
reference.element = element;
element.reference = reference;
}
}
class LinkingUnit {
final Reference reference;
final ast.CompilationUnitImpl node;
final LibraryOrAugmentationElementImpl container;
final CompilationUnitElementImpl element;
LinkingUnit({
required this.reference,
required this.node,
required this.container,
required this.element,
});
}
enum MacroDeclarationsPhaseStepResult {
nothing,
otherProgress,
topDeclaration,
}
class _CodeOptimizer extends macro.CodeOptimizer {
final LinkedElementFactory elementFactory;
final Map<Uri, Set<String>> exportedNames = {};
_CodeOptimizer({
required this.elementFactory,
});
@override
Set<String> getImportedNames(String uriStr) {
var uri = Uri.parse(uriStr);
var libraryElement = elementFactory.libraryOfUri(uri);
if (libraryElement != null) {
return exportedNames[uri] ??= libraryElement.exportedReferences
.map((exported) => exported.reference.name)
.toSet();
}
return const <String>{};
}
}
/// This class examines all the [InterfaceElement]s in a library and determines
/// which fields are promotable within that library.
class _FieldPromotability extends FieldPromotability<InterfaceElement,
FieldElement, PropertyAccessorElement> {
/// 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<InterfaceElement> getSuperclasses(InterfaceElement class_,
{required bool ignoreImplements}) {
List<InterfaceElement> result = [];
var supertype = class_.supertype;
if (supertype != null) {
result.add(supertype.element);
}
for (var m in class_.mixins) {
result.add(m.element);
}
if (!ignoreImplements) {
for (var interface in class_.interfaces) {
result.add(interface.element);
}
if (class_ is MixinElement) {
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.
for (var unitElement in _libraryBuilder.element.units) {
for (var class_ in unitElement.classes) {
_handleMembers(addClass(class_, isAbstract: class_.isAbstract), class_);
}
for (var enum_ in unitElement.enums) {
_handleMembers(addClass(enum_, isAbstract: false), enum_);
}
for (var mixin_ in unitElement.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 unitElement.extensionTypes) {
if (extensionType.augmentationTarget == null) {
var representation = extensionType.representation;
if (representation.name.startsWith('_')) {
representation.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.isPromotable = true;
}
}
_libraryBuilder.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<InterfaceElement> classInfo, InterfaceElementImpl class_) {
for (var field in class_.fields) {
if (field.isStatic || field.isSynthetic) {
continue;
}
var nonPromotabilityReason = addField(classInfo, field, field.name,
isFinal: field.isFinal,
isAbstract: field.isAbstract,
isExternal: field.isExternal);
if (enabled && nonPromotabilityReason == null) {
_potentiallyPromotableFields.add(field);
}
}
for (var accessor in class_.accessors) {
if (!accessor.isGetter || accessor.isStatic || accessor.isSynthetic) {
continue;
}
var nonPromotabilityReason = addGetter(classInfo, accessor, accessor.name,
isAbstract: accessor.isAbstract);
if (enabled && nonPromotabilityReason == null) {
_potentiallyPromotableFields
.add(accessor.variable2 as FieldElementImpl);
}
}
}
}
extension<T> on T? {
R? mapOrNull<R>(R Function(T) mapper) {
var self = this;
return self != null ? mapper(self) : null;
}
}