blob: 74e302cc393829e96df029f233eb437fd49c4ca0 [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/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/summary/api_signature.dart';
/// Return the bytes of the unlinked API signature of the given [unit].
///
/// If API signatures of two units are different, they may have different APIs.
List<int> computeUnlinkedApiSignature(CompilationUnit unit) {
var computer = _UnitApiSignatureComputer();
computer.compute(unit);
return computer.signature.toByteList();
}
class _UnitApiSignatureComputer {
static const int _kindConstructorDeclaration = 1;
static const int _kindFieldDeclaration = 2;
static const int _kindMethodDeclaration = 3;
static const int _nullNode = 0;
static const int _notNullNode = 1;
static const int _nullToken = 0;
static const int _notNullToken = 1;
final ApiSignature signature = ApiSignature();
void compute(CompilationUnit unit) {
signature.addFeatureSet(unit.featureSet);
signature.addInt(unit.directives.length);
unit.directives.forEach(_addNode);
signature.addInt(unit.declarations.length);
for (var declaration in unit.declarations) {
if (declaration is ClassOrMixinDeclaration) {
_addClassOrMixin(declaration);
} else if (declaration is FunctionDeclaration) {
var functionExpression = declaration.functionExpression;
_addTokens(
declaration.beginToken,
(functionExpression.parameters ?? declaration.name).endToken,
);
_addFunctionBodyModifiers(functionExpression.body);
} else if (declaration is TopLevelVariableDeclaration) {
_topLevelVariableDeclaration(declaration);
} else {
_addNode(declaration);
}
}
}
void _addClassOrMixin(ClassOrMixinDeclaration node) {
_addTokens(node.beginToken, node.leftBracket);
bool hasConstConstructor = node.members
.any((m) => m is ConstructorDeclaration && m.constKeyword != null);
signature.addInt(node.members.length);
for (var member in node.members) {
if (member is ConstructorDeclaration) {
signature.addInt(_kindConstructorDeclaration);
_addTokens(member.beginToken, member.parameters.endToken);
if (member.constKeyword != null) {
_addNodeList(member.initializers);
}
_addNode(member.redirectedConstructor);
} else if (member is FieldDeclaration) {
signature.addInt(_kindFieldDeclaration);
_fieldDeclaration(member, hasConstConstructor);
} else if (member is MethodDeclaration) {
signature.addInt(_kindMethodDeclaration);
_addTokens(
member.beginToken,
(member.parameters ?? member.name).endToken,
);
signature.addBool(member.body is EmptyFunctionBody);
_addFunctionBodyModifiers(member.body);
} else {
throw UnimplementedError('(${member.runtimeType}) $member');
}
}
_addToken(node.rightBracket);
}
void _addFunctionBodyModifiers(FunctionBody? node) {
if (node != null) {
signature.addBool(node.isSynchronous);
signature.addBool(node.isGenerator);
}
}
void _addNode(AstNode? node) {
if (node != null) {
signature.addInt(_notNullNode);
_addTokens(node.beginToken, node.endToken);
} else {
signature.addInt(_nullNode);
}
}
void _addNodeList(List<AstNode> nodes) {
for (var node in nodes) {
_addNode(node);
}
}
void _addToken(Token? token) {
if (token != null) {
signature.addInt(_notNullToken);
signature.addString(token.lexeme);
} else {
signature.addInt(_nullToken);
}
}
/// Appends tokens from [begin] (including), to [end] (also including).
void _addTokens(Token begin, Token end) {
if (begin is CommentToken) {
begin = begin.parent!;
}
Token? token = begin;
while (token != null) {
_addToken(token);
if (token == end) {
break;
}
var nextToken = token.next;
// Stop if EOF.
if (nextToken == token) {
break;
}
token = nextToken;
}
}
void _fieldDeclaration(FieldDeclaration node, bool hasConstConstructor) {
_addToken(node.abstractKeyword);
_addToken(node.covariantKeyword);
_addToken(node.externalKeyword);
_addToken(node.staticKeyword);
_addNodeList(node.metadata);
var variableList = node.fields;
var includeInitializers = variableList.type == null ||
variableList.isConst ||
hasConstConstructor && !node.isStatic && variableList.isFinal;
_variableList(variableList, includeInitializers);
}
void _topLevelVariableDeclaration(TopLevelVariableDeclaration node) {
_addToken(node.externalKeyword);
_addNodeList(node.metadata);
var variableList = node.variables;
var includeInitializers = variableList.type == null || variableList.isConst;
_variableList(variableList, includeInitializers);
}
void _variableList(VariableDeclarationList node, bool includeInitializers) {
_addToken(node.keyword);
_addToken(node.lateKeyword);
_addNode(node.type);
var variables = node.variables;
signature.addInt(variables.length);
for (var variable in variables) {
_addNode(variable.name);
signature.addBool(variable.initializer != null);
if (includeInitializers) {
_addNode(variable.initializer);
}
}
}
}