blob: 15c7bf2aa98e887e09fc9da94fbccbe9348e6d12 [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:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary2/core_types.dart';
import 'package:analyzer/src/summary2/lazy_ast.dart';
import 'package:analyzer/src/summary2/linked_bundle_context.dart';
import 'package:analyzer/src/summary2/linked_unit_context.dart';
import 'package:analyzer/src/summary2/reference.dart';
class LinkedElementFactory {
final AnalysisContext analysisContext;
final AnalysisSession analysisSession;
final Reference rootReference;
final Map<String, LinkedLibraryContext> libraryMap = {};
CoreTypes _coreTypes;
LinkedElementFactory(
this.analysisContext,
this.analysisSession,
this.rootReference,
) {
var dartCoreRef = rootReference.getChild('dart:core');
dartCoreRef.getChild('dynamic').element = DynamicElementImpl.instance;
dartCoreRef.getChild('Never').element = NeverElementImpl.instance;
}
CoreTypes get coreTypes {
return _coreTypes ??= CoreTypes(this);
}
Reference get dynamicRef {
return rootReference.getChild('dart:core').getChild('dynamic');
}
bool get hasDartCore {
return libraryMap.containsKey('dart:core');
}
void addBundle(LinkedBundleContext context) {
libraryMap.addAll(context.libraryMap);
}
Namespace buildExportNamespace(Uri uri) {
var exportedNames = <String, Element>{};
var exportedReferences = exportsOfLibrary('$uri');
for (var exportedReference in exportedReferences) {
var element = elementOfReference(exportedReference);
exportedNames[element.name] = element;
}
return Namespace(exportedNames);
}
Element elementOfReference(Reference reference) {
if (reference.element != null) {
return reference.element;
}
if (reference.parent == null) {
return null;
}
return _ElementRequest(this, reference).elementOfReference(reference);
}
List<Reference> exportsOfLibrary(String uriStr) {
var library = libraryMap[uriStr];
if (library == null) return const [];
var exportIndexList = library.node.exports;
var exportReferences = List<Reference>(exportIndexList.length);
for (var i = 0; i < exportIndexList.length; ++i) {
var index = exportIndexList[i];
var reference = library.context.referenceOfIndex(index);
exportReferences[i] = reference;
}
return exportReferences;
}
bool isLibraryUri(String uriStr) {
var libraryContext = libraryMap[uriStr];
return !libraryContext.definingUnit.hasPartOfDirective;
}
LibraryElementImpl libraryOfUri(String uriStr) {
var reference = rootReference.getChild(uriStr);
return elementOfReference(reference);
}
/// We have linked the bundle, and need to disconnect its libraries, so
/// that the client can re-add the bundle, this time read from bytes.
void removeBundle(LinkedBundleContext context) {
for (var uriStr in context.libraryMap.keys) {
libraryMap.remove(uriStr);
rootReference.removeChild(uriStr);
}
}
/// Set optional informative data for the unit.
void setInformativeData(
String libraryUriStr,
String unitUriStr,
List<UnlinkedInformativeData> informativeData,
) {
var libraryContext = libraryMap[libraryUriStr];
if (libraryContext != null) {
for (var unitContext in libraryContext.units) {
if (unitContext.uriStr == unitUriStr) {
unitContext.informativeData = informativeData;
return;
}
}
}
}
}
class _ElementRequest {
final LinkedElementFactory elementFactory;
final Reference input;
_ElementRequest(this.elementFactory, this.input);
ElementImpl elementOfReference(Reference reference) {
if (reference.element != null) {
return reference.element;
}
var parent2 = reference.parent.parent;
if (parent2 == null) {
return _createLibraryElement(reference);
}
var parentName = reference.parent.name;
if (parentName == '@class') {
var unit = elementOfReference(parent2);
return _class(unit, reference);
}
if (parentName == '@constructor') {
var class_ = elementOfReference(parent2);
return _constructor(class_, reference);
}
if (parentName == '@enum') {
var unit = elementOfReference(parent2);
return _enum(unit, reference);
}
if (parentName == '@extension') {
var unit = elementOfReference(parent2);
return _extension(unit, reference);
}
if (parentName == '@field') {
var enclosing = elementOfReference(parent2);
return _field(enclosing, reference);
}
if (parentName == '@function') {
CompilationUnitElementImpl enclosing = elementOfReference(parent2);
return _function(enclosing, reference);
}
if (parentName == '@getter' || parentName == '@setter') {
var enclosing = elementOfReference(parent2);
return _accessor(enclosing, reference);
}
if (parentName == '@method') {
var enclosing = elementOfReference(parent2);
return _method(enclosing, reference);
}
if (parentName == '@mixin') {
var unit = elementOfReference(parent2);
return _mixin(unit, reference);
}
if (parentName == '@parameter') {
ExecutableElementImpl enclosing = elementOfReference(parent2);
return _parameter(enclosing, reference);
}
if (parentName == '@prefix') {
LibraryElementImpl enclosing = elementOfReference(parent2);
return _prefix(enclosing, reference);
}
if (parentName == '@typeAlias') {
var unit = elementOfReference(parent2);
return _typeAlias(unit, reference);
}
if (parentName == '@typeParameter') {
var enclosing = elementOfReference(parent2);
if (enclosing is ParameterElement) {
(enclosing as ParameterElement).typeParameters;
} else {
(enclosing as TypeParameterizedElement).typeParameters;
}
assert(reference.element != null);
return reference.element;
}
if (parentName == '@unit') {
elementOfReference(parent2);
// Creating a library fills all its units.
assert(reference.element != null);
return reference.element;
}
if (reference.name == '@function' && parent2.name == '@typeAlias') {
var parent = reference.parent;
GenericTypeAliasElementImpl alias = elementOfReference(parent);
return alias.function;
}
throw StateError('Not found: $input');
}
PropertyAccessorElementImpl _accessor(
ElementImpl enclosing, Reference reference) {
if (enclosing is ClassElementImpl) {
enclosing.accessors;
// Requesting accessors sets elements for accessors and fields.
assert(reference.element != null);
return reference.element;
}
if (enclosing is CompilationUnitElementImpl) {
enclosing.accessors;
// Requesting accessors sets elements for accessors and variables.
assert(reference.element != null);
return reference.element;
}
if (enclosing is EnumElementImpl) {
enclosing.accessors;
// Requesting accessors sets elements for accessors and variables.
assert(reference.element != null);
return reference.element;
}
// Only classes and units have accessors.
throw StateError('${enclosing.runtimeType}');
}
ClassElementImpl _class(
CompilationUnitElementImpl unit, Reference reference) {
if (reference.node == null) {
_indexUnitElementDeclarations(unit);
assert(reference.node != null, '$reference');
}
ClassElementImpl.forLinkedNode(unit, reference, reference.node);
return reference.element;
}
ConstructorElementImpl _constructor(
ClassElementImpl enclosing, Reference reference) {
enclosing.constructors;
// Requesting constructors sets elements for all of them.
assert(reference.element != null);
return reference.element;
}
LibraryElementImpl _createLibraryElement(Reference reference) {
var uriStr = reference.name;
var sourceFactory = elementFactory.analysisContext.sourceFactory;
var librarySource = sourceFactory.forUri(uriStr);
// The URI cannot be resolved, we don't know the library.
if (librarySource == null) return null;
var libraryContext = elementFactory.libraryMap[uriStr];
if (libraryContext == null) {
throw ArgumentError('Missing library: $uriStr');
}
var libraryNode = libraryContext.node;
var hasName = libraryNode.name.isNotEmpty;
var definingUnitContext = libraryContext.definingUnit;
var libraryElement = LibraryElementImpl.forLinkedNode(
elementFactory.analysisContext,
elementFactory.analysisSession,
libraryNode.name,
hasName ? libraryNode.nameOffset : -1,
libraryNode.name.length,
definingUnitContext,
reference,
definingUnitContext.unit_withDeclarations,
);
var units = <CompilationUnitElementImpl>[];
var unitContainerRef = reference.getChild('@unit');
for (var unitContext in libraryContext.units) {
var unitNode = unitContext.unit_withDeclarations;
var unitSource = sourceFactory.forUri(unitContext.uriStr);
var unitElement = CompilationUnitElementImpl.forLinkedNode(
libraryElement,
unitContext,
unitContext.reference,
unitNode,
);
unitElement.lineInfo = unitNode.lineInfo;
unitElement.source = unitSource;
unitElement.librarySource = librarySource;
unitElement.uri = unitContext.partUriStr;
units.add(unitElement);
unitContainerRef.getChild(unitContext.uriStr).element = unitElement;
}
libraryElement.definingCompilationUnit = units[0];
libraryElement.parts = units.skip(1).toList();
reference.element = libraryElement;
var typeProvider = elementFactory.analysisContext.typeProvider;
if (typeProvider != null) {
libraryElement.createLoadLibraryFunction(typeProvider);
}
return libraryElement;
}
EnumElementImpl _enum(CompilationUnitElementImpl unit, Reference reference) {
if (reference.node == null) {
_indexUnitElementDeclarations(unit);
assert(reference.node != null, '$reference');
}
EnumElementImpl.forLinkedNode(unit, reference, reference.node);
return reference.element;
}
ExtensionElementImpl _extension(
CompilationUnitElementImpl unit, Reference reference) {
if (reference.node == null) {
_indexUnitElementDeclarations(unit);
assert(reference.node != null, '$reference');
}
ExtensionElementImpl.forLinkedNode(unit, reference, reference.node);
return reference.element;
}
FieldElementImpl _field(ClassElementImpl enclosing, Reference reference) {
enclosing.fields;
// Requesting fields sets elements for all fields.
assert(reference.element != null);
return reference.element;
}
Element _function(CompilationUnitElementImpl enclosing, Reference reference) {
enclosing.functions;
assert(reference.element != null);
return reference.element;
}
void _indexUnitElementDeclarations(CompilationUnitElementImpl unit) {
var unitContext = unit.linkedContext;
var unitRef = unit.reference;
var unitNode = unit.linkedNode;
_indexUnitDeclarations(unitContext, unitRef, unitNode);
}
MethodElementImpl _method(ClassElementImpl enclosing, Reference reference) {
enclosing.methods;
// Requesting methods sets elements for all of them.
assert(reference.element != null);
return reference.element;
}
MixinElementImpl _mixin(
CompilationUnitElementImpl unit, Reference reference) {
if (reference.node == null) {
_indexUnitElementDeclarations(unit);
assert(reference.node != null, '$reference');
}
MixinElementImpl.forLinkedNode(unit, reference, reference.node);
return reference.element;
}
Element _parameter(ExecutableElementImpl enclosing, Reference reference) {
enclosing.parameters;
assert(reference.element != null);
return reference.element;
}
PrefixElementImpl _prefix(LibraryElementImpl library, Reference reference) {
for (var import in library.imports) {
import.prefix;
}
assert(reference.element != null);
return reference.element;
}
GenericTypeAliasElementImpl _typeAlias(
CompilationUnitElementImpl unit, Reference reference) {
if (reference.node == null) {
_indexUnitElementDeclarations(unit);
assert(reference.node != null, '$reference');
}
GenericTypeAliasElementImpl.forLinkedNode(unit, reference, reference.node);
return reference.element;
}
/// Index nodes for which we choose to create elements individually,
/// for example [ClassDeclaration], so that its [Reference] has the node,
/// and we can call the [ClassElementImpl] constructor.
static void _indexUnitDeclarations(
LinkedUnitContext unitContext,
Reference unitRef,
CompilationUnit unitNode,
) {
var classRef = unitRef.getChild('@class');
var enumRef = unitRef.getChild('@enum');
var extensionRef = unitRef.getChild('@extension');
var functionRef = unitRef.getChild('@function');
var mixinRef = unitRef.getChild('@mixin');
var typeAliasRef = unitRef.getChild('@typeAlias');
var variableRef = unitRef.getChild('@variable');
for (var declaration in unitNode.declarations) {
if (declaration is ClassDeclaration) {
var name = declaration.name.name;
classRef.getChild(name).node = declaration;
} else if (declaration is ClassTypeAlias) {
var name = declaration.name.name;
classRef.getChild(name).node = declaration;
} else if (declaration is ExtensionDeclaration) {
var refName = LazyExtensionDeclaration.get(declaration).refName;
extensionRef.getChild(refName).node = declaration;
} else if (declaration is EnumDeclaration) {
var name = declaration.name.name;
enumRef.getChild(name).node = declaration;
} else if (declaration is FunctionDeclaration) {
var name = declaration.name.name;
functionRef.getChild(name).node = declaration;
} else if (declaration is FunctionTypeAlias) {
var name = declaration.name.name;
typeAliasRef.getChild(name).node = declaration;
} else if (declaration is GenericTypeAlias) {
var name = declaration.name.name;
typeAliasRef.getChild(name).node = declaration;
} else if (declaration is MixinDeclaration) {
var name = declaration.name.name;
mixinRef.getChild(name).node = declaration;
} else if (declaration is TopLevelVariableDeclaration) {
for (var variable in declaration.variables.variables) {
var name = variable.name.name;
variableRef.getChild(name).node = declaration;
}
}
}
}
}