| // 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 { |
| final ApiSignature signature = ApiSignature(); |
| |
| 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) { |
| var lastInitializer = member.constKeyword != null && |
| member.initializers != null && |
| member.initializers.isNotEmpty |
| ? member.initializers.last |
| : null; |
| addTokens( |
| member.beginToken, |
| (lastInitializer ?? member.parameters ?? member.name).endToken, |
| ); |
| } else if (member is FieldDeclaration) { |
| var variableList = member.fields; |
| addVariables( |
| member, |
| variableList, |
| !member.isStatic && variableList.isFinal && hasConstConstructor, |
| ); |
| } else if (member is MethodDeclaration) { |
| addTokens( |
| member.beginToken, |
| (member.parameters ?? member.name).endToken, |
| ); |
| signature.addBool(member.body is EmptyFunctionBody); |
| addFunctionBodyModifiers(member.body); |
| } else { |
| addNode(member); |
| } |
| } |
| |
| addToken(node.rightBracket); |
| } |
| |
| void addFunctionBodyModifiers(FunctionBody node) { |
| signature.addBool(node.isSynchronous); |
| signature.addBool(node.isGenerator); |
| } |
| |
| void addNode(AstNode node) { |
| addTokens(node.beginToken, node.endToken); |
| } |
| |
| void addToken(Token token) { |
| signature.addString(token.lexeme); |
| } |
| |
| /// Appends tokens from [begin] (including), to [end] (also including). |
| void addTokens(Token begin, Token end) { |
| if (begin is CommentToken) { |
| begin = (begin as CommentToken).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 addVariables( |
| AstNode node, |
| VariableDeclarationList variableList, |
| bool includeInitializers, |
| ) { |
| if (variableList.type == null || |
| variableList.isConst || |
| includeInitializers) { |
| addTokens(node.beginToken, node.endToken); |
| } else { |
| addTokens(node.beginToken, variableList.type.endToken); |
| |
| signature.addInt(variableList.variables.length); |
| for (var variable in variableList.variables) { |
| addTokens(variable.beginToken, variable.name.endToken); |
| signature.addBool(variable.initializer != null); |
| addToken(variable.endToken.next); // `,` or `;` |
| } |
| } |
| } |
| |
| void compute(CompilationUnit unit) { |
| 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) { |
| addVariables(declaration, declaration.variables, false); |
| } else { |
| addNode(declaration); |
| } |
| } |
| } |
| } |