blob: 588fa11a5e0a25f8e947c0f1800888b0aa863858 [file] [log] [blame]
// Copyright (c) 2016, 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/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart';
import 'package:analyzer/src/summary/api_signature.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary/public_namespace_computer.dart';
import 'package:analyzer/src/summary/summarize_const_expr.dart';
/// Serialize all the declarations in [compilationUnit] to an unlinked summary.
///
/// [serializeInferrableFields] indicates whether field initializers and closure
/// bodies should be serialized to facilitate type inference.
UnlinkedUnitBuilder serializeAstUnlinked(CompilationUnit compilationUnit,
{bool serializeInferrableFields: true}) {
return new _SummarizeAstVisitor(serializeInferrableFields)
.serializeCompilationUnit(compilationUnit);
}
/// Instances of this class keep track of intermediate state during
/// serialization of a single constant [Expression].
class _ConstExprSerializer extends AbstractConstExprSerializer {
final _SummarizeAstVisitor visitor;
/// If the expression being serialized can contain closures, map whose
/// keys are the offsets of local function nodes representing those closures,
/// and whose values are indices of those local functions relative to their
/// siblings.
final Map<int, int> localClosureIndexMap;
/// The names of local variables and parameters that are in scope.
/// This is a list so that we can handle nesting by pushing and popping values
/// at the end of it.
final List<String> variableNames;
_ConstExprSerializer(bool forConst, this.visitor, this.localClosureIndexMap,
List<String> variableNames)
: variableNames = variableNames ?? [],
super(forConst);
@override
bool isParameterName(String name) {
return variableNames?.contains(name) ?? false;
}
@override
void serializeAnnotation(Annotation annotation) {
Identifier name = annotation.name;
EntityRefBuilder constructor;
if (name is PrefixedIdentifier && annotation.constructorName == null) {
constructor = serializeConstructorRef(name.prefix, null, name.identifier);
} else {
constructor = serializeConstructorRef(
annotation.name, null, annotation.constructorName);
}
if (annotation.arguments == null) {
references.add(constructor);
operations.add(UnlinkedExprOperation.pushReference);
} else {
serializeInstanceCreation(constructor, annotation.arguments, false);
}
}
@override
EntityRefBuilder serializeConstructorRef(Identifier typeName,
TypeArgumentList typeArguments, SimpleIdentifier name) {
EntityRefBuilder typeBuilder = serializeTypeName(typeName, typeArguments);
if (name == null) {
return typeBuilder;
} else {
int nameRef =
visitor.serializeReference(typeBuilder.reference, name.name);
return new EntityRefBuilder(
reference: nameRef, typeArguments: typeBuilder.typeArguments);
}
}
@override
List<int> serializeFunctionExpression(FunctionExpression functionExpression) {
int localIndex;
if (localClosureIndexMap == null) {
return null;
} else {
localIndex = localClosureIndexMap[functionExpression.offset];
assert(localIndex != null);
return <int>[0, localIndex];
}
}
@override
EntityRefBuilder serializeGenericFunctionType(GenericFunctionType node) =>
visitor.serializeGenericFunctionType(node);
EntityRefBuilder serializeIdentifier(Identifier identifier) {
EntityRefBuilder b = new EntityRefBuilder();
if (identifier is SimpleIdentifier) {
int index = visitor.serializeSimpleReference(identifier.name);
if (index < 0) {
b.paramReference = -index;
} else {
b.reference = index;
}
} else if (identifier is PrefixedIdentifier) {
int prefix = visitor.serializeSimpleReference(identifier.prefix.name);
if (prefix < 0) {
throw new StateError('Invalid type parameter usage: $identifier}');
}
b.reference =
visitor.serializeReference(prefix, identifier.identifier.name);
} else {
throw new StateError(
'Unexpected identifier type: ${identifier.runtimeType}');
}
return b;
}
@override
EntityRefBuilder serializeIdentifierSequence(Expression expr) {
if (expr is Identifier) {
AstNode parent = expr.parent;
if (parent is MethodInvocation &&
parent.methodName == expr &&
parent.target != null) {
int targetId = serializeIdentifierSequence(parent.target).reference;
int nameId = visitor.serializeReference(targetId, expr.name);
return new EntityRefBuilder(reference: nameId);
}
return serializeIdentifier(expr);
}
if (expr is PropertyAccess) {
int targetId = serializeIdentifierSequence(expr.target).reference;
int nameId = visitor.serializeReference(targetId, expr.propertyName.name);
return new EntityRefBuilder(reference: nameId);
} else {
throw new StateError('Unexpected node type: ${expr.runtimeType}');
}
}
@override
EntityRefBuilder serializeTypeName(
Identifier name, TypeArgumentList arguments) {
return visitor.serializeTypeName(name, arguments);
}
}
/// A [_Scope] represents a set of name/value pairs defined locally within a
/// limited span of a compilation unit. (Note that the spec also uses the term
/// "scope" to refer to the set of names defined at top level within a
/// compilation unit, but we do not use [_Scope] for that purpose).
class _Scope {
/// Names defined in this scope, and their meanings.
Map<String, _ScopedEntity> _definedNames = <String, _ScopedEntity>{};
/// Look up the meaning associated with the given [name], and return it. If
/// [name] is not defined in this scope, return `null`.
_ScopedEntity operator [](String name) => _definedNames[name];
/// Let the given [name] refer to [entity] within this scope.
void operator []=(String name, _ScopedEntity entity) {
_definedNames[name] = entity;
}
}
/// A [_ScopedClassMember] is a [_ScopedEntity] refers to a member of a class.
class _ScopedClassMember extends _ScopedEntity {
/// The name of the class.
final String className;
_ScopedClassMember(this.className);
}
/// Base class for entities that can live inside a scope.
abstract class _ScopedEntity {}
/// A [_ScopedTypeParameter] is a [_ScopedEntity] that refers to a type
/// parameter of a class, typedef, or executable.
class _ScopedTypeParameter extends _ScopedEntity {
/// Index of the type parameter within this scope. Since summaries use De
/// Bruijn indices to refer to type parameters, which count upwards from the
/// innermost bound name, the last type parameter in the scope has an index of
/// 1, and each preceding type parameter has the next higher index.
final int index;
_ScopedTypeParameter(this.index);
}
/// Visitor used to create a summary from an AST.
class _SummarizeAstVisitor extends RecursiveAstVisitor {
/// Indicates whether non-const field initializers and closure bodies should
/// be serialized to facilitate type inference.
///
/// For one-phase summary generation, the only field initializers that need to
/// be serialized are those involved in constants, since type inference is
/// performed using the AST representation.
final bool _serializeInferrableFields;
/// List of objects which should be written to [UnlinkedUnit.classes].
final List<UnlinkedClassBuilder> classes = <UnlinkedClassBuilder>[];
/// List of objects which should be written to [UnlinkedUnit.enums].
final List<UnlinkedEnumBuilder> enums = <UnlinkedEnumBuilder>[];
/// List of objects which should be written to [UnlinkedUnit.executables],
/// [UnlinkedClass.executables] or [UnlinkedExecutable.localFunctions].
List<UnlinkedExecutableBuilder> executables = <UnlinkedExecutableBuilder>[];
/// List of objects which should be written to [UnlinkedUnit.exports].
final List<UnlinkedExportNonPublicBuilder> exports =
<UnlinkedExportNonPublicBuilder>[];
/// Whether the current class declaration has a `const` constructor.
bool enclosingClassHasConstConstructor = false;
/// List of objects which should be written to [UnlinkedUnit.mixins].
final List<UnlinkedClassBuilder> mixins = <UnlinkedClassBuilder>[];
/// List of names of methods, getters, setters, and operators that are
/// super-invoked in the current mixin declaration.
Set<String> mixinSuperInvokedNames;
/// List of objects which should be written to [UnlinkedUnit.parts].
final List<UnlinkedPartBuilder> parts = <UnlinkedPartBuilder>[];
/// List of objects which should be written to [UnlinkedUnit.typedefs].
final List<UnlinkedTypedefBuilder> typedefs = <UnlinkedTypedefBuilder>[];
/// List of objects which should be written to [UnlinkedUnit.variables] or
/// [UnlinkedClass.fields].
List<UnlinkedVariableBuilder> variables = <UnlinkedVariableBuilder>[];
/// The unlinked portion of the "imports table". This is the list of objects
/// which should be written to [UnlinkedUnit.imports].
final List<UnlinkedImportBuilder> unlinkedImports = <UnlinkedImportBuilder>[];
/// The unlinked portion of the "references table". This is the list of
/// objects which should be written to [UnlinkedUnit.references].
final List<UnlinkedReferenceBuilder> unlinkedReferences =
<UnlinkedReferenceBuilder>[new UnlinkedReferenceBuilder()];
/// List of [_Scope]s currently in effect. This is used to resolve type names
/// to type parameters within classes, typedefs, and executables, as well as
/// references to class members.
final List<_Scope> scopes = <_Scope>[];
/// True if 'dart:core' has been explicitly imported.
bool hasCoreBeenImported = false;
/// Names referenced by this compilation unit. Structured as a map from
/// prefix index to (map from name to reference table index), where "prefix
/// index" means the index into [UnlinkedUnit.references] of the prefix (or
/// `null` if there is no prefix), and "reference table index" means the index
/// into [UnlinkedUnit.references] for the name itself.
final Map<int, Map<String, int>> nameToReference = <int, Map<String, int>>{};
/// True if the 'dart:core' library is been summarized.
bool isCoreLibrary = false;
/// True is a [PartOfDirective] was found, so the unit is a part.
bool isPartOf = false;
/// If the library has a library directive, the library name derived from it.
/// Otherwise `null`.
String libraryName;
/// If the library has a library directive, the offset of the library name.
/// Otherwise `null`.
int libraryNameOffset;
/// If the library has a library directive, the length of the library name, as
/// it appears in the source file. Otherwise `null`.
int libraryNameLength;
/// If the library has a library directive, the documentation comment for it
/// (if any). Otherwise `null`.
UnlinkedDocumentationCommentBuilder libraryDocumentationComment;
/// If the library has a library directive, the annotations for it (if any).
/// Otherwise `null`.
List<UnlinkedExpr> libraryAnnotations = const <UnlinkedExprBuilder>[];
/// The number of slot ids which have been assigned to this compilation unit.
int numSlots = 0;
/// The [Block] that is being visited now, or `null` for non-local contexts.
Block enclosingBlock = null;
/// If an expression is being serialized which can contain closures, map whose
/// keys are the offsets of local function nodes representing those closures,
/// and whose values are indices of those local functions relative to their
/// siblings.
Map<int, int> _localClosureIndexMap;
/// Indicates whether closure function bodies should be serialized. This flag
/// is set while visiting the bodies of initializer expressions that will be
/// needed by type inference.
bool _serializeClosureBodyExprs = false;
/// The set of variable names which are currently in scope.
List<String> _variableNames = [];
/// Indicates whether parameters found during visitors might inherit
/// covariance.
bool _parametersMayInheritCovariance = false;
_SummarizeAstVisitor(this._serializeInferrableFields);
/// Create a slot id for storing a propagated or inferred type or const cycle
/// info.
int assignSlot() => ++numSlots;
/// Build a [_Scope] object containing the names defined within the body of a
/// class declaration.
_Scope buildClassMemberScope(
String className, NodeList<ClassMember> members) {
_Scope scope = new _Scope();
for (ClassMember member in members) {
if (member is MethodDeclaration) {
if (member.isSetter || member.isOperator) {
// We don't have to handle setters or operators because the only
// things we look up are type names and identifiers.
} else {
scope[member.name.name] = new _ScopedClassMember(className);
}
} else if (member is FieldDeclaration) {
for (VariableDeclaration field in member.fields.variables) {
// A field declaration introduces two names, one with a trailing `=`.
// We don't have to worry about the one with a trailing `=` because
// the only things we look up are type names and identifiers.
scope[field.name.name] = new _ScopedClassMember(className);
}
}
}
return scope;
}
/// Serialize the given list of [annotations]. If there are no annotations,
/// the empty list is returned.
List<UnlinkedExprBuilder> serializeAnnotations(
NodeList<Annotation> annotations) {
if (annotations == null || annotations.isEmpty) {
return const <UnlinkedExprBuilder>[];
}
return annotations.map((Annotation a) {
// Closures can't appear inside annotations, so we don't need a
// localClosureIndexMap.
Map<int, int> localClosureIndexMap = null;
_ConstExprSerializer serializer =
new _ConstExprSerializer(true, this, localClosureIndexMap, null);
try {
serializer.serializeAnnotation(a);
} on StateError {
return new UnlinkedExprBuilder()..isValidConst = false;
}
return serializer.toBuilder(a.atSign.next, a.endToken);
}).toList();
}
/// Serialize a [ClassDeclaration] or [ClassTypeAlias] into an [UnlinkedClass]
/// and store the result in [classes].
void serializeClass(
AstNode node,
Token abstractKeyword,
String name,
int nameOffset,
TypeParameterList typeParameters,
TypeName superclass,
WithClause withClause,
ImplementsClause implementsClause,
NodeList<ClassMember> members,
bool isMixinApplication,
Comment documentationComment,
NodeList<Annotation> annotations) {
int oldScopesLength = scopes.length;
enclosingClassHasConstConstructor = node is ClassDeclaration &&
node.members
.any((m) => m is ConstructorDeclaration && m.constKeyword != null);
List<UnlinkedExecutableBuilder> oldExecutables = executables;
executables = <UnlinkedExecutableBuilder>[];
List<UnlinkedVariableBuilder> oldVariables = variables;
variables = <UnlinkedVariableBuilder>[];
_TypeParameterScope typeParameterScope = new _TypeParameterScope();
scopes.add(typeParameterScope);
UnlinkedClassBuilder b = new UnlinkedClassBuilder();
b.name = name;
b.nameOffset = nameOffset;
b.isMixinApplication = isMixinApplication;
b.typeParameters =
serializeTypeParameters(typeParameters, typeParameterScope);
if (_shouldAssignNotSimplyBoundedSlot(typeParameters)) {
b.notSimplyBoundedSlot = assignSlot();
}
if (superclass != null) {
b.supertype = serializeType(superclass);
} else {
b.hasNoSupertype = isCoreLibrary && name == 'Object';
}
if (withClause != null) {
b.mixins = withClause.mixinTypes.map(serializeMixedInType).toList();
}
if (implementsClause != null) {
b.interfaces = implementsClause.interfaces.map(serializeType).toList();
}
if (members != null) {
scopes.add(buildClassMemberScope(name, members));
for (ClassMember member in members) {
member.accept(this);
}
scopes.removeLast();
}
b.executables = executables;
b.fields = variables;
b.isAbstract = abstractKeyword != null;
b.documentationComment = serializeDocumentation(documentationComment);
b.annotations = serializeAnnotations(annotations);
b.codeRange = serializeCodeRange(node);
classes.add(b);
scopes.removeLast();
assert(scopes.length == oldScopesLength);
executables = oldExecutables;
variables = oldVariables;
}
/// Create a [CodeRangeBuilder] for the given [node].
CodeRangeBuilder serializeCodeRange(AstNode node) {
return new CodeRangeBuilder(offset: node.offset, length: node.length);
}
/// Serialize a [Combinator] into an [UnlinkedCombinator].
UnlinkedCombinatorBuilder serializeCombinator(Combinator combinator) {
UnlinkedCombinatorBuilder b = new UnlinkedCombinatorBuilder();
if (combinator is ShowCombinator) {
b.shows =
combinator.shownNames.map((SimpleIdentifier id) => id.name).toList();
b.offset = combinator.offset;
b.end = combinator.end;
} else if (combinator is HideCombinator) {
b.hides =
combinator.hiddenNames.map((SimpleIdentifier id) => id.name).toList();
} else {
throw new StateError(
'Unexpected combinator type: ${combinator.runtimeType}');
}
return b;
}
/// Main entry point for serializing an AST.
UnlinkedUnitBuilder serializeCompilationUnit(
CompilationUnit compilationUnit) {
compilationUnit.directives.accept(this);
if (!hasCoreBeenImported) {
unlinkedImports.add(new UnlinkedImportBuilder(isImplicit: true));
}
compilationUnit.declarations.accept(this);
UnlinkedUnitBuilder b = new UnlinkedUnitBuilder();
b.lineStarts = compilationUnit.lineInfo?.lineStarts;
b.isPartOf = isPartOf;
b.libraryName = libraryName;
b.libraryNameOffset = libraryNameOffset;
b.libraryNameLength = libraryNameLength;
b.libraryDocumentationComment = libraryDocumentationComment;
b.libraryAnnotations = libraryAnnotations;
b.codeRange = serializeCodeRange(compilationUnit);
b.classes = classes;
b.enums = enums;
b.executables = executables;
b.exports = exports;
b.imports = unlinkedImports;
b.mixins = mixins;
b.parts = parts;
b.references = unlinkedReferences;
b.typedefs = typedefs;
b.variables = variables;
b.publicNamespace = computePublicNamespace(compilationUnit);
_computeApiSignature(b);
return b;
}
/// Serialize the given [expression], creating an [UnlinkedExprBuilder].
UnlinkedExprBuilder serializeConstExpr(
bool forConst, Map<int, int> localClosureIndexMap, Expression expression,
[List<String> variableNames]) {
_ConstExprSerializer serializer = new _ConstExprSerializer(
forConst, this, localClosureIndexMap, variableNames);
serializer.serialize(expression);
return serializer.toBuilder(expression.beginToken, expression.endToken);
}
/// Serialize a [Comment] node into an [UnlinkedDocumentationComment] object.
UnlinkedDocumentationCommentBuilder serializeDocumentation(
Comment documentationComment) {
if (documentationComment == null) {
return null;
}
String text = documentationComment.tokens
.map((Token t) => t.toString())
.join('\n')
.replaceAll('\r\n', '\n');
return new UnlinkedDocumentationCommentBuilder(text: text);
}
/// Return an entity reference builder representing the type 'dynamic'.
EntityRefBuilder serializeDynamic() {
EntityRefBuilder builder = new EntityRefBuilder();
builder.reference = serializeReference(null, 'dynamic');
return builder;
}
/// Serialize an [EnumConstantDeclaration] into an [UnlinkedEnumValue].
UnlinkedEnumValueBuilder serializeEnumConstantDeclaration(
EnumConstantDeclaration node) {
return new UnlinkedEnumValueBuilder(
annotations: serializeAnnotations(node.metadata),
documentationComment: serializeDocumentation(node.documentationComment),
name: node.name.name,
nameOffset: node.name.offset);
}
/// Serialize a [FunctionDeclaration] or [MethodDeclaration] into an
/// [UnlinkedExecutable].
///
/// If [serializeBodyExpr] is `true`, then the function definition is stored
/// in [UnlinkedExecutableBuilder.bodyExpr].
UnlinkedExecutableBuilder serializeExecutable(
AstNode node,
String name,
int nameOffset,
bool isGetter,
bool isSetter,
TypeAnnotation returnType,
FormalParameterList formalParameters,
FunctionBody body,
bool isTopLevel,
bool isDeclaredStatic,
Comment documentationComment,
NodeList<Annotation> annotations,
TypeParameterList typeParameters,
bool isExternal,
bool serializeBodyExpr,
bool serializeBody) {
int oldScopesLength = scopes.length;
_TypeParameterScope typeParameterScope = new _TypeParameterScope();
scopes.add(typeParameterScope);
UnlinkedExecutableBuilder b = new UnlinkedExecutableBuilder();
String nameString = name;
if (isGetter) {
b.kind = UnlinkedExecutableKind.getter;
} else if (isSetter) {
b.kind = UnlinkedExecutableKind.setter;
nameString = '$nameString=';
} else {
b.kind = UnlinkedExecutableKind.functionOrMethod;
}
b.isExternal = isExternal;
b.isAbstract = !isExternal && body is EmptyFunctionBody;
b.isAsynchronous = body.isAsynchronous;
b.isGenerator = body.isGenerator;
b.name = nameString;
b.nameOffset = nameOffset;
b.typeParameters =
serializeTypeParameters(typeParameters, typeParameterScope);
if (!isTopLevel) {
b.isStatic = isDeclaredStatic;
}
b.returnType = serializeType(returnType);
bool isSemanticallyStatic = isTopLevel || isDeclaredStatic;
if (formalParameters != null) {
bool oldMayInheritCovariance = _parametersMayInheritCovariance;
_parametersMayInheritCovariance = !isTopLevel && !isDeclaredStatic;
b.parameters = formalParameters.parameters
.map((FormalParameter p) => p.accept(this) as UnlinkedParamBuilder)
.toList();
_parametersMayInheritCovariance = oldMayInheritCovariance;
if (!isSemanticallyStatic) {
for (int i = 0; i < formalParameters.parameters.length; i++) {
if (!b.parameters[i].isFunctionTyped &&
b.parameters[i].type == null) {
b.parameters[i].inferredTypeSlot = assignSlot();
}
}
}
}
b.documentationComment = serializeDocumentation(documentationComment);
b.annotations = serializeAnnotations(annotations);
b.codeRange = serializeCodeRange(node);
if (returnType == null && !isSemanticallyStatic) {
b.inferredReturnTypeSlot = assignSlot();
}
b.visibleOffset = enclosingBlock?.offset;
b.visibleLength = enclosingBlock?.length;
int oldVariableNamesLength = _variableNames.length;
if (formalParameters != null && formalParameters.parameters.isNotEmpty) {
_variableNames.addAll(formalParameters.parameters
.map((FormalParameter p) => p.identifier.name));
}
serializeFunctionBody(
b, null, body, serializeBodyExpr, serializeBody, false);
if (mixinSuperInvokedNames != null) {
body?.accept(new MixinSuperInvokedNamesCollector(mixinSuperInvokedNames));
}
_variableNames.length = oldVariableNamesLength;
scopes.removeLast();
assert(scopes.length == oldScopesLength);
return b;
}
/// Record local functions and variables into the given executable. The given
/// [body] is usually an actual [FunctionBody], but may be an [Expression]
/// when we process a synthetic variable initializer function.
///
/// If [initializers] is non-`null`, closures occurring inside the
/// initializers are serialized first.
///
/// If [serializeBodyExpr] is `true`, then the function definition is stored
/// in [UnlinkedExecutableBuilder.bodyExpr], and closures occurring inside
/// [initializers] and [body] have their function bodies serialized as well.
///
/// The return value is a map whose keys are the offsets of local function
/// nodes representing closures inside [initializers] and [body], and whose
/// values are the indices of those local functions relative to their
/// siblings.
Map<int, int> serializeFunctionBody(
UnlinkedExecutableBuilder b,
List<ConstructorInitializer> initializers,
AstNode body,
bool serializeBodyExpr,
bool serializeBody,
bool forConst) {
if (body is BlockFunctionBody || body is ExpressionFunctionBody) {
for (UnlinkedParamBuilder parameter in b.parameters) {
if (!parameter.isInitializingFormal) {
parameter.visibleOffset = body.offset;
parameter.visibleLength = body.length;
}
}
}
List<UnlinkedExecutableBuilder> oldExecutables = executables;
Map<int, int> oldLocalClosureIndexMap = _localClosureIndexMap;
bool oldSerializeClosureBodyExprs = _serializeClosureBodyExprs;
executables = <UnlinkedExecutableBuilder>[];
_localClosureIndexMap = <int, int>{};
_serializeClosureBodyExprs =
serializeBodyExpr && _serializeInferrableFields;
if (initializers != null) {
for (ConstructorInitializer initializer in initializers) {
initializer.accept(this);
}
}
if (serializeBody) {
body.accept(this);
}
if (serializeBodyExpr) {
if (body is Expression) {
b.bodyExpr = serializeConstExpr(
forConst, _localClosureIndexMap, body, _variableNames);
} else if (body is ExpressionFunctionBody) {
b.bodyExpr = serializeConstExpr(
forConst, _localClosureIndexMap, body.expression, _variableNames);
} else {
// TODO(paulberry): serialize other types of function bodies.
}
}
b.localFunctions = executables;
Map<int, int> localClosureIndexMap = _localClosureIndexMap;
executables = oldExecutables;
_localClosureIndexMap = oldLocalClosureIndexMap;
_serializeClosureBodyExprs = oldSerializeClosureBodyExprs;
return localClosureIndexMap;
}
/// Serialize the return type and parameters of a function-typed formal
/// parameter and store them in [b].
void serializeFunctionTypedParameterDetails(UnlinkedParamBuilder b,
TypeAnnotation returnType, FormalParameterList parameters) {
EntityRefBuilder serializedReturnType = serializeType(returnType);
if (serializedReturnType != null) {
b.type = serializedReturnType;
}
bool oldMayInheritCovariance = _parametersMayInheritCovariance;
_parametersMayInheritCovariance = false;
b.parameters = parameters.parameters
.map((FormalParameter p) => p.accept(this) as UnlinkedParamBuilder)
.toList();
_parametersMayInheritCovariance = oldMayInheritCovariance;
}
/// Serialize a generic function type.
EntityRefBuilder serializeGenericFunctionType(GenericFunctionType node) {
_TypeParameterScope typeParameterScope = new _TypeParameterScope();
scopes.add(typeParameterScope);
EntityRefBuilder b = new EntityRefBuilder();
b.entityKind = EntityRefKind.genericFunctionType;
b.typeParameters =
serializeTypeParameters(node.typeParameters, typeParameterScope);
b.syntheticReturnType = node.returnType == null
? serializeDynamic()
: serializeType(node.returnType);
b.syntheticParams = node.parameters.parameters
.map((FormalParameter p) => p.accept(this) as UnlinkedParamBuilder)
.toList();
scopes.removeLast();
return b;
}
/// If the given [expression] is not `null`, serialize it as an
/// [UnlinkedExecutableBuilder], otherwise return `null`.
///
/// If [serializeBodyExpr] is `true`, then the initializer expression is
/// stored in [UnlinkedExecutableBuilder.bodyExpr].
UnlinkedExecutableBuilder serializeInitializerFunction(
Expression expression, bool serializeBodyExpr, bool forConst) {
if (expression == null) {
return null;
}
UnlinkedExecutableBuilder initializer =
new UnlinkedExecutableBuilder(nameOffset: expression.offset);
serializeFunctionBody(
initializer, null, expression, serializeBodyExpr, true, forConst);
initializer.inferredReturnTypeSlot = assignSlot();
return initializer;
}
/// Serialize a type name that appears in a "with" clause to an [EntityRef].
EntityRefBuilder serializeMixedInType(TypeAnnotation node) {
var builder = serializeType(node);
if (builder != null && builder.typeArguments.isEmpty) {
// Type arguments may get inferred so we need to assign a slot to hold the
// complete inferred mixed in type.
builder.refinedSlot = assignSlot();
}
return builder;
}
/// Serialize a [MixinDeclaration] into an [UnlinkedClass]
/// and store the result in [mixins].
void serializeMixin(
AstNode node,
String name,
int nameOffset,
TypeParameterList typeParameters,
OnClause onClause,
ImplementsClause implementsClause,
NodeList<ClassMember> members,
Comment documentationComment,
NodeList<Annotation> annotations) {
List<UnlinkedExecutableBuilder> oldExecutables = executables;
executables = <UnlinkedExecutableBuilder>[];
mixinSuperInvokedNames = new Set<String>();
List<UnlinkedVariableBuilder> oldVariables = variables;
variables = <UnlinkedVariableBuilder>[];
int oldScopesLength = scopes.length;
_TypeParameterScope typeParameterScope = new _TypeParameterScope();
scopes.add(typeParameterScope);
UnlinkedClassBuilder b = new UnlinkedClassBuilder();
b.name = name;
b.nameOffset = nameOffset;
b.typeParameters =
serializeTypeParameters(typeParameters, typeParameterScope);
if (onClause != null) {
b.superclassConstraints =
onClause.superclassConstraints.map(serializeType).toList();
}
if (implementsClause != null) {
b.interfaces = implementsClause.interfaces.map(serializeType).toList();
}
if (members != null) {
scopes.add(buildClassMemberScope(name, members));
for (ClassMember member in members) {
member.accept(this);
}
scopes.removeLast();
}
b.executables = executables;
b.fields = variables;
b.superInvokedNames = mixinSuperInvokedNames.toList();
b.documentationComment = serializeDocumentation(documentationComment);
b.annotations = serializeAnnotations(annotations);
b.codeRange = serializeCodeRange(node);
mixins.add(b);
scopes.removeLast();
assert(scopes.length == oldScopesLength);
executables = oldExecutables;
mixinSuperInvokedNames = null;
variables = oldVariables;
}
/// Serialize a [FieldFormalParameter], [FunctionTypedFormalParameter], or
/// [SimpleFormalParameter] into an [UnlinkedParam].
UnlinkedParamBuilder serializeParameter(NormalFormalParameter node) {
UnlinkedParamBuilder b = new UnlinkedParamBuilder();
b.name = node.identifier?.name;
b.nameOffset = node.identifier?.offset;
b.annotations = serializeAnnotations(node.metadata);
b.codeRange = serializeCodeRange(node);
b.isExplicitlyCovariant = node.covariantKeyword != null;
b.isFinal = node.isFinal;
if (_parametersMayInheritCovariance) {
b.inheritsCovariantSlot = assignSlot();
}
if (node.isRequired) {
b.kind = UnlinkedParamKind.required;
} else if (node.isOptionalPositional) {
b.kind = UnlinkedParamKind.positional;
} else if (node.isNamed) {
b.kind = UnlinkedParamKind.named;
} else {
// ignore: deprecated_member_use_from_same_package
throw new StateError('Unexpected parameter kind: ${node.kind}');
}
return b;
}
/// Serialize a reference to a top level name declared elsewhere, by adding an
/// entry to the references table if necessary. If [prefixIndex] is not null,
/// the reference is associated with the prefix having the given index in the
/// references table.
int serializeReference(int prefixIndex, String name) => nameToReference
.putIfAbsent(prefixIndex, () => <String, int>{})
.putIfAbsent(name, () {
int index = unlinkedReferences.length;
unlinkedReferences.add(new UnlinkedReferenceBuilder(
prefixReference: prefixIndex, name: name));
return index;
});
/// Serialize a reference to a name declared either at top level or in a
/// nested scope.
///
/// References to type parameters are returned as negative numbers.
int serializeSimpleReference(String name) {
int indexOffset = 0;
for (int i = scopes.length - 1; i >= 0; i--) {
_Scope scope = scopes[i];
_ScopedEntity entity = scope[name];
if (entity != null) {
if (entity is _ScopedClassMember) {
return serializeReference(
serializeReference(null, entity.className), name);
} else if (entity is _ScopedTypeParameter) {
int paramReference = indexOffset + entity.index;
return -paramReference;
}
}
if (scope is _TypeParameterScope) {
indexOffset += scope.length;
}
}
return serializeReference(null, name);
}
/// Serialize a type name (which might be defined in a nested scope, at top
/// level within this library, or at top level within an imported library) to
/// a [EntityRef]. Note that this method does the right thing if the
/// name doesn't refer to an entity other than a type (e.g. a class member).
EntityRefBuilder serializeType(TypeAnnotation node) {
if (node is TypeName) {
return serializeTypeName(node?.name, node?.typeArguments);
} else if (node is GenericFunctionType) {
return serializeGenericFunctionType(node);
} else if (node != null) {
throw new ArgumentError('Cannot serialize a ${node.runtimeType}');
}
return null;
}
/// Serialize a type name (which might be defined in a nested scope, at top
/// level within this library, or at top level within an imported library) to
/// a [EntityRef]. Note that this method does the right thing if the
/// name doesn't refer to an entity other than a type (e.g. a class member).
EntityRefBuilder serializeTypeName(
Identifier identifier, TypeArgumentList typeArguments) {
if (identifier == null) {
return null;
} else {
EntityRefBuilder b = new EntityRefBuilder();
if (identifier is SimpleIdentifier) {
String name = identifier.name;
int indexOffset = 0;
for (int i = scopes.length - 1; i >= 0; i--) {
_Scope scope = scopes[i];
_ScopedEntity entity = scope[name];
if (entity != null) {
if (entity is _ScopedTypeParameter) {
b.paramReference = indexOffset + entity.index;
return b;
} else {
// None of the other things that can be declared in local scopes
// are types, so this is an error and should be treated as a
// reference to `dynamic`.
b.reference = serializeReference(null, 'dynamic');
return b;
}
}
if (scope is _TypeParameterScope) {
indexOffset += scope.length;
}
}
b.reference = serializeReference(null, name);
} else if (identifier is PrefixedIdentifier) {
int prefixIndex = serializeSimpleReference(identifier.prefix.name);
if (prefixIndex < 0) {
// Type parameters are not expected here, so this is an error and the
// type should be treated as a reference to `dynamic`.
b.reference = serializeReference(null, 'dynamic');
return b;
} else {
b.reference =
serializeReference(prefixIndex, identifier.identifier.name);
}
} else {
throw new StateError(
'Unexpected identifier type: ${identifier.runtimeType}');
}
if (typeArguments != null) {
b.typeArguments = typeArguments.arguments.map(serializeType).toList();
}
return b;
}
}
/// Serialize the given [typeParameters] into a list of [UnlinkedTypeParam]s,
/// and also store them in [typeParameterScope].
List<UnlinkedTypeParamBuilder> serializeTypeParameters(
TypeParameterList typeParameters,
_TypeParameterScope typeParameterScope) {
if (typeParameters != null) {
for (int i = 0; i < typeParameters.typeParameters.length; i++) {
TypeParameter typeParameter = typeParameters.typeParameters[i];
typeParameterScope[typeParameter.name.name] =
new _ScopedTypeParameter(typeParameters.typeParameters.length - i);
}
return typeParameters.typeParameters.map(visitTypeParameter).toList();
}
return const <UnlinkedTypeParamBuilder>[];
}
/// Serialize the given [variables] into [UnlinkedVariable]s, and store them
/// in [this.variables].
void serializeVariables(
VariableDeclarationList variables,
bool isDeclaredStatic,
Comment documentationComment,
NodeList<Annotation> annotations,
bool isField) {
bool isCovariant = isField
? (variables.parent as FieldDeclaration).covariantKeyword != null
: false;
for (int i = 0; i < variables.variables.length; i++) {
VariableDeclaration variable = variables.variables[i];
UnlinkedVariableBuilder b = new UnlinkedVariableBuilder();
b.isConst = variables.isConst;
b.isCovariant = isCovariant;
b.isFinal = variables.isFinal;
b.isStatic = isDeclaredStatic;
b.name = variable.name.name;
b.nameOffset = variable.name.offset;
b.type = serializeType(variables.type);
b.documentationComment = serializeDocumentation(documentationComment);
b.annotations = serializeAnnotations(annotations);
{
int offset = (i == 0 ? variables.parent : variable).offset;
int length = variable.end - offset;
b.codeRange = new CodeRangeBuilder(offset: offset, length: length);
}
bool serializeBodyExpr = variable.isConst ||
_serializeInferrableFields && variables.type == null ||
isField &&
!isDeclaredStatic &&
variables.isFinal &&
enclosingClassHasConstConstructor;
b.initializer = serializeInitializerFunction(
variable.initializer, serializeBodyExpr, b.isConst);
if (isField && !isDeclaredStatic && !variables.isFinal) {
b.inheritsCovariantSlot = assignSlot();
}
if (variable.initializer != null &&
(variables.isFinal || variables.isConst)) {
b.propagatedTypeSlot = assignSlot();
}
bool isSemanticallyStatic = !isField || isDeclaredStatic;
if (variables.type == null &&
(variable.initializer != null || !isSemanticallyStatic)) {
b.inferredTypeSlot = assignSlot();
}
this.variables.add(b);
}
}
@override
void visitBlock(Block node) {
Block oldBlock = enclosingBlock;
enclosingBlock = node;
super.visitBlock(node);
enclosingBlock = oldBlock;
}
@override
void visitClassDeclaration(ClassDeclaration node) {
TypeName superclass =
node.extendsClause == null ? null : node.extendsClause.superclass;
serializeClass(
node,
node.abstractKeyword,
node.name.name,
node.name.offset,
node.typeParameters,
superclass,
node.withClause,
node.implementsClause,
node.members,
false,
node.documentationComment,
node.metadata);
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
serializeClass(
node,
node.abstractKeyword,
node.name.name,
node.name.offset,
node.typeParameters,
node.superclass,
node.withClause,
node.implementsClause,
null,
true,
node.documentationComment,
node.metadata);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
UnlinkedExecutableBuilder b = new UnlinkedExecutableBuilder();
if (node.name != null) {
b.name = node.name.name;
b.nameOffset = node.name.offset;
b.periodOffset = node.period.offset;
b.nameEnd = node.name.end;
} else {
b.nameOffset = node.returnType.offset;
}
b.parameters = node.parameters.parameters
.map((FormalParameter p) => p.accept(this) as UnlinkedParamBuilder)
.toList();
b.kind = UnlinkedExecutableKind.constructor;
if (node.factoryKeyword != null) {
b.isFactory = true;
if (node.redirectedConstructor != null) {
b.isRedirectedConstructor = true;
TypeName typeName = node.redirectedConstructor.type;
// Closures can't appear inside factory constructor redirections, so we
// don't need a localClosureIndexMap.
Map<int, int> localClosureIndexMap = null;
b.redirectedConstructor =
new _ConstExprSerializer(true, this, localClosureIndexMap, null)
.serializeConstructorRef(typeName.name, typeName.typeArguments,
node.redirectedConstructor.name);
}
} else {
for (ConstructorInitializer initializer in node.initializers) {
if (initializer is RedirectingConstructorInvocation) {
b.isRedirectedConstructor = true;
b.redirectedConstructorName = initializer.constructorName?.name;
}
}
}
if (node.constKeyword != null) {
b.isConst = true;
b.constCycleSlot = assignSlot();
}
b.isExternal =
node.externalKeyword != null || node.body is NativeFunctionBody;
b.documentationComment = serializeDocumentation(node.documentationComment);
b.annotations = serializeAnnotations(node.metadata);
b.codeRange = serializeCodeRange(node);
Map<int, int> localClosureIndexMap = serializeFunctionBody(b,
node.initializers, node.body, node.constKeyword != null, false, false);
if (node.constKeyword != null) {
List<String> constructorParameterNames =
node.parameters.parameters.map((p) => p.identifier.name).toList();
b.constantInitializers = node.initializers
.map((ConstructorInitializer initializer) =>
serializeConstructorInitializer(initializer, (Expression expr) {
return serializeConstExpr(true, localClosureIndexMap, expr,
constructorParameterNames);
}))
.toList();
}
executables.add(b);
}
@override
UnlinkedParamBuilder visitDefaultFormalParameter(
DefaultFormalParameter node) {
UnlinkedParamBuilder b =
node.parameter.accept(this) as UnlinkedParamBuilder;
b.initializer = serializeInitializerFunction(node.defaultValue, true, true);
if (node.defaultValue != null) {
b.defaultValueCode = node.defaultValue.toSource();
}
b.codeRange = serializeCodeRange(node);
return b;
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
UnlinkedEnumBuilder b = new UnlinkedEnumBuilder();
b.name = node.name.name;
b.nameOffset = node.name.offset;
b.values = node.constants.map(serializeEnumConstantDeclaration).toList();
b.documentationComment = serializeDocumentation(node.documentationComment);
b.annotations = serializeAnnotations(node.metadata);
b.codeRange = serializeCodeRange(node);
enums.add(b);
}
@override
void visitExportDirective(ExportDirective node) {
UnlinkedExportNonPublicBuilder b = new UnlinkedExportNonPublicBuilder(
uriOffset: node.uri.offset, uriEnd: node.uri.end, offset: node.offset);
b.annotations = serializeAnnotations(node.metadata);
exports.add(b);
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
serializeVariables(node.fields, node.staticKeyword != null,
node.documentationComment, node.metadata, true);
}
@override
UnlinkedParamBuilder visitFieldFormalParameter(FieldFormalParameter node) {
UnlinkedParamBuilder b = serializeParameter(node);
b.isInitializingFormal = true;
if (node.type != null || node.parameters != null) {
b.isFunctionTyped = node.parameters != null;
if (node.parameters != null) {
serializeFunctionTypedParameterDetails(b, node.type, node.parameters);
} else {
b.type = serializeType(node.type);
}
}
return b;
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
executables.add(serializeExecutable(
node,
node.name.name,
node.name.offset,
node.isGetter,
node.isSetter,
node.returnType,
node.functionExpression.parameters,
node.functionExpression.body,
true,
false,
node.documentationComment,
node.metadata,
node.functionExpression.typeParameters,
node.externalKeyword != null ||
node.functionExpression.body is NativeFunctionBody,
false,
node.parent is FunctionDeclarationStatement));
}
@override
void visitFunctionExpression(FunctionExpression node) {
if (node.parent is! FunctionDeclaration) {
if (_localClosureIndexMap != null) {
_localClosureIndexMap[node.offset] = executables.length;
}
executables.add(serializeExecutable(
node,
null,
node.offset,
false,
false,
null,
node.parameters,
node.body,
false,
false,
null,
null,
node.typeParameters,
false,
_serializeClosureBodyExprs,
true));
}
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
int oldScopesLength = scopes.length;
_TypeParameterScope typeParameterScope = new _TypeParameterScope();
scopes.add(typeParameterScope);
UnlinkedTypedefBuilder b = new UnlinkedTypedefBuilder();
b.name = node.name.name;
b.nameOffset = node.name.offset;
b.typeParameters =
serializeTypeParameters(node.typeParameters, typeParameterScope);
b.notSimplyBoundedSlot = assignSlot();
EntityRefBuilder serializedReturnType = serializeType(node.returnType);
if (serializedReturnType != null) {
b.returnType = serializedReturnType;
}
b.parameters = node.parameters.parameters
.map((FormalParameter p) => p.accept(this) as UnlinkedParamBuilder)
.toList();
b.documentationComment = serializeDocumentation(node.documentationComment);
b.annotations = serializeAnnotations(node.metadata);
b.codeRange = serializeCodeRange(node);
typedefs.add(b);
scopes.removeLast();
assert(scopes.length == oldScopesLength);
}
@override
UnlinkedParamBuilder visitFunctionTypedFormalParameter(
FunctionTypedFormalParameter node) {
UnlinkedParamBuilder b = serializeParameter(node);
b.isFunctionTyped = true;
serializeFunctionTypedParameterDetails(b, node.returnType, node.parameters);
return b;
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
int oldScopesLength = scopes.length;
_TypeParameterScope typeParameterScope = new _TypeParameterScope();
scopes.add(typeParameterScope);
UnlinkedTypedefBuilder b = new UnlinkedTypedefBuilder();
b.style = TypedefStyle.genericFunctionType;
b.name = node.name.name;
b.nameOffset = node.name.offset;
b.typeParameters =
serializeTypeParameters(node.typeParameters, typeParameterScope);
b.notSimplyBoundedSlot = assignSlot();
GenericFunctionType functionType = node.functionType;
EntityRefBuilder serializedType = functionType == null
? null
: serializeGenericFunctionType(functionType);
if (serializedType != null) {
b.returnType = serializedType;
}
b.documentationComment = serializeDocumentation(node.documentationComment);
b.annotations = serializeAnnotations(node.metadata);
b.codeRange = serializeCodeRange(node);
typedefs.add(b);
scopes.removeLast();
assert(scopes.length == oldScopesLength);
}
@override
void visitImportDirective(ImportDirective node) {
UnlinkedImportBuilder b = new UnlinkedImportBuilder();
b.annotations = serializeAnnotations(node.metadata);
if (node.uri.stringValue == 'dart:core') {
hasCoreBeenImported = true;
}
b.offset = node.offset;
b.combinators = node.combinators.map(serializeCombinator).toList();
b.configurations = node.configurations.map(serializeConfiguration).toList();
if (node.prefix != null) {
b.prefixReference = serializeReference(null, node.prefix.name);
b.prefixOffset = node.prefix.offset;
}
b.isDeferred = node.deferredKeyword != null;
b.uri = node.uri.stringValue;
b.uriOffset = node.uri.offset;
b.uriEnd = node.uri.end;
unlinkedImports.add(b);
}
@override
void visitLibraryDirective(LibraryDirective node) {
libraryName =
node.name.components.map((SimpleIdentifier id) => id.name).join('.');
libraryNameOffset = node.name.offset;
libraryNameLength = node.name.length;
isCoreLibrary = libraryName == 'dart.core';
libraryDocumentationComment =
serializeDocumentation(node.documentationComment);
libraryAnnotations = serializeAnnotations(node.metadata);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
executables.add(serializeExecutable(
node,
node.name.name,
node.name.offset,
node.isGetter,
node.isSetter,
node.returnType,
node.parameters,
node.body,
false,
node.isStatic,
node.documentationComment,
node.metadata,
node.typeParameters,
node.externalKeyword != null || node.body is NativeFunctionBody,
false,
false));
}
@override
visitMixinDeclaration(MixinDeclaration node) {
serializeMixin(
node,
node.name.name,
node.name.offset,
node.typeParameters,
node.onClause,
node.implementsClause,
node.members,
node.documentationComment,
node.metadata);
}
@override
void visitPartDirective(PartDirective node) {
parts.add(new UnlinkedPartBuilder(
uriOffset: node.uri.offset,
uriEnd: node.uri.end,
annotations: serializeAnnotations(node.metadata)));
}
@override
void visitPartOfDirective(PartOfDirective node) {
isCoreLibrary = node.libraryName?.name == 'dart.core' ||
node.uri?.stringValue == 'core.dart';
isPartOf = true;
}
@override
UnlinkedParamBuilder visitSimpleFormalParameter(SimpleFormalParameter node) {
UnlinkedParamBuilder b = serializeParameter(node);
b.type = serializeType(node.type);
return b;
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
serializeVariables(
node.variables, false, node.documentationComment, node.metadata, false);
}
@override
UnlinkedTypeParamBuilder visitTypeParameter(TypeParameter node) {
UnlinkedTypeParamBuilder b = new UnlinkedTypeParamBuilder();
b.name = node.name.name;
b.nameOffset = node.name.offset;
if (node.bound != null) {
b.bound = serializeType(node.bound);
}
b.annotations = serializeAnnotations(node.metadata);
b.codeRange = serializeCodeRange(node);
return b;
}
@override
void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
// TODO(scheglov) Remove when we stop serializing local functions.
}
/// Determines whether a class declaration with the given [typeParameters]
/// needs to be assigned a slot to indicate whether it is simply bounded.
bool _shouldAssignNotSimplyBoundedSlot(TypeParameterList typeParameters) {
if (typeParameters == null) return false;
for (var typeParameter in typeParameters.typeParameters) {
if (typeParameter.bound != null) return true;
}
return false;
}
/// Compute the API signature of the unit and record it.
static void _computeApiSignature(UnlinkedUnitBuilder b) {
ApiSignature apiSignature = new ApiSignature();
b.collectApiSignature(apiSignature);
b.apiSignature = apiSignature.toByteList();
}
}
/// A [_TypeParameterScope] is a [_Scope] which defines [_ScopedTypeParameter]s.
class _TypeParameterScope extends _Scope {
/// Get the number of [_ScopedTypeParameter]s defined in this
/// [_TypeParameterScope].
int get length => _definedNames.length;
}