blob: ea0c3289fdb0b18aee8fb867701868c786ee6759 [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.
library serialization.summarize_ast;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/type.dart' show DartType;
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';
import 'package:front_end/src/base/api_signature.dart';
/**
* Serialize all the declarations in [compilationUnit] to an unlinked summary.
*/
UnlinkedUnitBuilder serializeAstUnlinked(CompilationUnit compilationUnit) {
return new _SummarizeAstVisitor().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;
/**
* If the expression being serialized appears inside a function body, the names
* of parameters that are in scope. Otherwise `null`.
*/
final Set<String> parameterNames;
_ConstExprSerializer(bool forConst, this.visitor, this.localClosureIndexMap,
this.parameterNames)
: super(forConst);
@override
bool isParameterName(String name) {
return parameterNames?.contains(name) ?? false;
}
@override
void serializeAnnotation(Annotation annotation) {
Identifier name = annotation.name;
EntityRefBuilder constructor;
if (name is PrefixedIdentifier && annotation.constructorName == null) {
constructor =
serializeConstructorRef(null, name.prefix, null, name.identifier);
} else {
constructor = serializeConstructorRef(
null, 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(DartType type, Identifier typeName,
TypeArgumentList typeArguments, SimpleIdentifier name) {
EntityRefBuilder typeBuilder =
serializeTypeName(type, 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(
DartType type, 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 {
/**
* 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>[];
/**
* 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;
/**
* If a closure function body is being serialized, the set of closure
* parameter names which are currently in scope. Otherwise `null`.
*/
Set<String> _parameterNames;
/**
* Indicates whether parameters found during visitors might inherit
* covariance.
*/
bool _parametersMayInheritCovariance = false;
/**
* 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();
}).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;
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 (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.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,
[Set<String> parameterNames]) {
_ConstExprSerializer serializer = new _ConstExprSerializer(
forConst, this, localClosureIndexMap, parameterNames);
serializer.serialize(expression);
return serializer.toBuilder();
}
/**
* 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 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;
Set<String> oldParameterNames = _parameterNames;
if (formalParameters != null && formalParameters.parameters.isNotEmpty) {
_parameterNames =
_parameterNames == null ? new Set<String>() : _parameterNames.toSet();
_parameterNames.addAll(formalParameters.parameters
.map((FormalParameter p) => p.identifier.name));
}
serializeFunctionBody(
b, null, body, serializeBodyExpr, serializeBody, false);
_parameterNames = oldParameterNames;
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;
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, _parameterNames);
} else if (body is ExpressionFunctionBody) {
b.bodyExpr = serializeConstExpr(
forConst, _localClosureIndexMap, body.expression, _parameterNames);
} 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 [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
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 (VariableDeclaration variable in variables.variables) {
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);
b.codeRange = serializeCodeRange(variables.parent);
bool serializeBodyExpr = variable.isConst ||
variable.isFinal && isField && !isDeclaredStatic ||
variables.type == null;
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(null, 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) {
Set<String> constructorParameterNames =
node.parameters.parameters.map((p) => p.identifier.name).toSet();
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((EnumConstantDeclaration value) => new UnlinkedEnumValueBuilder(
documentationComment:
serializeDocumentation(value.documentationComment),
name: value.name.name,
nameOffset: value.name.offset))
.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);
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);
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
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.
}
/**
* 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;
}