Remove package analyzer_fe_comparison.

This package has outlived its usefulness and is beginning to bit-rot.

Change-Id: I823a1ed4d31765c54633a868d5dfc36106aca562
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/139024
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2020-03-11 15:20:59 +00:00 committed by commit-bot@chromium.org
parent 92b1057f3f
commit 4b1fd4f670
8 changed files with 0 additions and 1040 deletions

View file

@ -1,48 +0,0 @@
// Copyright (c) 2018, 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 'dart:async';
import 'dart:io';
import 'package:analyzer/src/command_line/arguments.dart';
import 'package:analyzer_fe_comparison/comparison.dart';
import 'package:args/args.dart';
import 'package:path/path.dart' as path;
/// Compares the analyzer and front_end behavior when compiling a program.
main(List<String> args) async {
ArgResults options = _parseArgs(args);
var sourcePaths = options.rest;
if (sourcePaths.length != 1) {
throw new StateError('Exactly one source file must be specified.');
}
var sourcePath = sourcePaths[0];
var scriptPath = Platform.script.toFilePath();
var sdkRepoPath =
path.normalize(path.join(path.dirname(scriptPath), '..', '..', '..'));
var buildPath = await _findBuildDir(sdkRepoPath, 'ReleaseX64');
var dillPath = path.join(buildPath, 'vm_platform_strong.dill');
var packagesFilePath = path.join(sdkRepoPath, '.packages');
await compareTestPrograms(sourcePath, dillPath, packagesFilePath);
}
Future<String> _findBuildDir(String sdkRepoPath, String targetName) async {
for (var subdirName in ['out', 'xcodebuild']) {
var candidatePath = path.join(sdkRepoPath, subdirName, targetName);
if (await new Directory(candidatePath).exists()) {
return candidatePath;
}
}
throw new StateError('Cannot find build directory');
}
ArgResults _parseArgs(List<String> args) {
var parser = new ArgParser(allowTrailingOptions: true);
parser.addOption('dart-sdk', help: 'The path to the Dart SDK.');
if (args.contains('--ignore-unrecognized-flags')) {
args = filterUnknownArguments(args, parser);
}
return parser.parse(args);
}

View file

@ -1,42 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2018, 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.
# Run analyzer_fe_comparison on the Dart VM. This script assumes the Dart
# repo's directory structure.
function follow_links() {
file="$1"
while [ -h "$file" ]; do
# On Mac OS, readlink -f doesn't work.
file="$(readlink "$file")"
done
echo "$file"
}
# Unlike $0, $BASH_SOURCE points to the absolute path of this file.
PROG_NAME="$(follow_links "$BASH_SOURCE")"
# Find directories.
PKG_BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
DART_ROOT="$(cd "${PKG_BIN_DIR}/../../.." ; pwd -P)"
SDK_DIR="${DART_ROOT}/sdk"
BIN_DIR="${SDK_DIR}/bin"
SDK_ARG="--dart-sdk=$SDK_DIR"
DART="$BIN_DIR/dart"
unset EXTRA_VM_OPTIONS
declare -a EXTRA_VM_OPTIONS
# We allow extra vm options to be passed in through an environment variable.
if [[ $DART_VM_OPTIONS ]]; then
read -a OPTIONS <<< "$DART_VM_OPTIONS"
EXTRA_VM_OPTIONS+=("${OPTIONS[@]}")
fi
COMPARE_PROGRAMS="$PKG_BIN_DIR/compare_programs.dart"
exec "$DART" "--packages=$DART_ROOT/.packages" "${EXTRA_VM_OPTIONS[@]}" "$COMPARE_PROGRAMS" "$SDK_ARG" "$@"

View file

@ -1,69 +0,0 @@
// Copyright (c) 2018, 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_fe_comparison/src/analyzer.dart' as analyzer;
import 'package:analyzer_fe_comparison/src/comparison_node.dart';
import 'package:analyzer_fe_comparison/src/kernel.dart' as kernel;
import 'package:path/path.dart' as path;
/// Compares the analyzer and kernel representations of a package, and prints
/// the resulting diff.
void comparePackages(
String platformPath, String projectLibPath, String packagesFilePath) async {
ComparisonNode analyzerNode = await analyzer.analyzePackage(projectLibPath);
var packagesFileUri = Uri.file(packagesFilePath);
var inputs = <Uri>[];
for (var library in analyzerNode.children) {
inputs.add(Uri.parse(library.text));
}
var platformUri = Uri.file(platformPath);
ComparisonNode kernelNode =
await kernel.analyzePackage(inputs, packagesFileUri, platformUri);
print(ComparisonNode.diff(kernelNode, analyzerNode, 'CFE', 'analyzer'));
}
/// Compares the analyzer and kernel representations of a test file, and prints
/// the resulting diff.
///
/// Only libraries reached by a "file:" URI are compared.
void compareTestPrograms(
String sourcePath, String platformPath, String packagesFilePath) async {
var packagesFileUri = Uri.file(packagesFilePath);
var platformUri = Uri.file(platformPath);
ComparisonNode kernelNode = await kernel.analyzeProgram(
path.toUri(sourcePath),
packagesFileUri,
platformUri,
(uri) => uri.scheme == 'file');
if (kernelNode.text == 'Error occurred') {
// TODO(paulberry): really we should verify that the analyzer detects an
// error as well. But that's not easy to do right now because we use the
// front end to chase imports so that we know which files to pass to the
// analyzer, and we can't rely on the front end import chasing when an error
// occurred.
print('No differences found (skipped due to front end compilation error)');
return;
}
String startingPath;
var inputs = <String>[];
for (var library in kernelNode.children) {
var filePath = path.fromUri(Uri.parse(library.text));
if (startingPath == null) {
startingPath = path.dirname(filePath);
} else {
while (!path.isWithin(startingPath, filePath)) {
startingPath = path.dirname(startingPath);
}
}
inputs.add(filePath);
}
ComparisonNode analyzerNode =
await analyzer.analyzeFiles(startingPath, inputs);
if (kernelNode == analyzerNode) {
print('No differences found!');
} else {
print('Differences found:');
print(ComparisonNode.diff(kernelNode, analyzerNode, 'CFE', 'analyzer'));
}
}

View file

@ -1,341 +0,0 @@
// Copyright (c) 2018, 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 'dart:async';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/analysis/uri_converter.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart' show SourceKind;
import 'package:analyzer_fe_comparison/src/comparison_node.dart';
/// Analyzes the files in [filePaths] using the analyzer, and returns a
/// [ComparisonNode] representing them.
Future<ComparisonNode> analyzeFiles(
String startingPath, List<String> filePaths) async {
var driver = await _AnalyzerDriver.create(startingPath);
return driver.analyzeFiles(filePaths);
}
/// Analyzes the package located at [libPath] using the analyzer, and returns a
/// [ComparisonNode] representing it.
Future<ComparisonNode> analyzePackage(String libPath) async {
var driver = await _AnalyzerDriver.create(libPath);
return driver.analyzePackage();
}
class _AnalyzerDriver {
final AnalysisSession _session;
final TypeProvider _typeProvider;
final UriConverter _uriConverter;
final ContextRoot _contextRoot;
_AnalyzerDriver._(
this._session, this._typeProvider, this._uriConverter, this._contextRoot);
Future<ComparisonNode> analyzeFiles(Iterable<String> filePaths) async {
var libraryNodes = <ComparisonNode>[];
for (var filePath in filePaths) {
var kind = await _session.getSourceKind(filePath);
if (kind == SourceKind.LIBRARY) {
var importUri = _uriConverter.pathToUri(filePath);
var libraryElement =
await _session.getLibraryByUri(importUri.toString());
var childNodes = <ComparisonNode>[];
if (libraryElement.name.isNotEmpty) {
childNodes.add(ComparisonNode('name=${libraryElement.name}'));
}
for (var compilationUnit in libraryElement.units) {
var unitResult =
await _session.getResolvedAst(compilationUnit.source.fullName);
_AnalyzerVisitor(_typeProvider, childNodes)
._visitList(unitResult.unit.declarations);
}
libraryNodes
.add(ComparisonNode.sorted(importUri.toString(), childNodes));
}
}
return ComparisonNode.sorted('Component', libraryNodes);
}
Future<ComparisonNode> analyzePackage() async {
return analyzeFiles(_contextRoot.analyzedFiles());
}
static Future<_AnalyzerDriver> create(String startingPath) async {
var contextCollection =
AnalysisContextCollection(includedPaths: [startingPath]);
var contexts = contextCollection.contexts;
if (contexts.length != 1) {
throw new StateError('Expected exactly one context');
}
var context = contexts[0];
var session = context.currentSession;
var typeProvider = await session.typeProvider;
var uriConverter = session.uriConverter;
var contextRoot = context.contextRoot;
return _AnalyzerDriver._(session, typeProvider, uriConverter, contextRoot);
}
}
/// Visitor for serializing the contents of an analyzer AST into
/// ComparisonNodes.
///
/// Results are accumulated into [_resultNodes].
class _AnalyzerVisitor extends UnifyingAstVisitor<void> {
final TypeProvider _typeProvider;
final List<ComparisonNode> _resultNodes;
_AnalyzerVisitor(this._typeProvider, this._resultNodes);
@override
void visitClassDeclaration(ClassDeclaration node) {
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._handleClassOrClassTypeAlias(node.declaredElement);
visitor._visitList(node.members);
_resultNodes
.add(ComparisonNode.sorted('Class ${node.name.name}', children));
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._handleClassOrClassTypeAlias(node.declaredElement);
_resultNodes.add(
ComparisonNode.sorted('MixinApplication ${node.name.name}', children));
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._visitParameters(node.parameters);
_resultNodes.add(ComparisonNode.sorted(
'Constructor ${node.name?.name ?? '(unnamed)'}', children));
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
var children = <ComparisonNode>[];
for (var enumValue in node.constants) {
children.add(ComparisonNode('EnumValue ${enumValue.name.name}'));
}
_resultNodes.add(ComparisonNode.sorted('Enum ${node.name.name}', children));
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
node.fields.accept(this);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
String kind;
if (node.isGetter) {
kind = 'Getter';
} else if (node.isSetter) {
kind = 'Setter';
} else {
// Kernel calls top level functions "methods".
kind = 'Method';
}
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._visitTypeParameters(node.declaredElement.typeParameters);
visitor._visitParameters(node.functionExpression.parameters);
children
.add(_translateType('Return type: ', node.declaredElement.returnType));
_resultNodes
.add(ComparisonNode.sorted('$kind ${node.name.name}', children));
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_visitTypedef(node);
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
_visitTypedef(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
var name = node.name.name;
String kind;
if (node.isGetter) {
kind = 'Getter';
} else if (node.isSetter) {
kind = 'Setter';
} else if (node.isOperator) {
kind = 'Operator';
if (name == '-' && node.declaredElement.parameters.isEmpty) {
name = 'unary-';
}
} else {
kind = 'Method';
}
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._visitTypeParameters(node.declaredElement.typeParameters);
visitor._visitParameters(node.parameters);
children
.add(_translateType('Return type: ', node.declaredElement.returnType));
_resultNodes.add(ComparisonNode.sorted('$kind $name', children));
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
// At present, kernel doesn't distinguish between mixin and class
// declarations. So treat the mixin as a class.
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._handleClassOrClassTypeAlias(node.declaredElement);
visitor._visitList(node.members);
_resultNodes
.add(ComparisonNode.sorted('Mixin ${node.name.name}', children));
}
@override
Null visitNode(AstNode node) {
throw new UnimplementedError('AnalyzerVisitor: ${node.runtimeType}');
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
node.variables.accept(this);
}
@override
void visitVariableDeclarationList(VariableDeclarationList node) {
for (var variableDeclaration in node.variables) {
var children = <ComparisonNode>[];
children.add(
_translateType('Type: ', variableDeclaration.declaredElement.type));
// Kernel calls both fields and top level variable declarations "fields".
_resultNodes.add(ComparisonNode.sorted(
'Field ${variableDeclaration.name.name}', children));
}
}
void _handleClassOrClassTypeAlias(ClassElement element) {
_visitTypeParameters(element.typeParameters);
if (element.isMixin) {
for (int i = 0; i < element.superclassConstraints.length; i++) {
_resultNodes
.add(_translateType('On $i: ', element.superclassConstraints[i]));
}
} else {
if (element.supertype != null) {
_resultNodes.add(_translateType('Extends: ', element.supertype));
}
for (int i = 0; i < element.mixins.length; i++) {
_resultNodes.add(_translateType('Mixin $i: ', element.mixins[i]));
}
}
for (int i = 0; i < element.interfaces.length; i++) {
_resultNodes
.add(_translateType('Implements $i: ', element.interfaces[i]));
}
}
/// Converts the analyzer representation of a type into a ComparisonNode.
ComparisonNode _translateType(String prefix, DartType type) {
if (type is InterfaceType) {
var children = <ComparisonNode>[];
children
.add(ComparisonNode('Library: ${type.element.librarySource.uri}'));
for (int i = 0; i < type.typeArguments.length; i++) {
children.add(_translateType('Type arg $i: ', type.typeArguments[i]));
}
return ComparisonNode('${prefix}InterfaceType ${type.name}', children);
}
if (type is TypeParameterType) {
// TODO(paulberry): disambiguate if needed.
return ComparisonNode('${prefix}TypeParameterType: ${type.name}');
}
if (type.isDynamic) {
return ComparisonNode('${prefix}Dynamic');
}
if (type.isVoid) {
return ComparisonNode('${prefix}Void');
}
if (type is FunctionType) {
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
visitor._visitTypeParameters(type.typeFormals);
children.add(_translateType('Return type: ', type.returnType));
int positionalParameterIndex = 0;
for (var parameterElement in type.parameters) {
var kind = parameterElement.isNotOptional
? 'Required'
: parameterElement.isOptionalPositional ? 'Optional' : 'Named';
var name = parameterElement.isNamed
? parameterElement.name
: '${positionalParameterIndex++}';
children.add(
_translateType('$kind parameter $name: ', parameterElement.type));
}
return ComparisonNode.sorted('${prefix}FunctionType', children);
}
throw new UnimplementedError('_translateType: ${type.runtimeType}');
}
/// Visits all the nodes in [nodes].
void _visitList(List<AstNode> nodes) {
for (var astNode in nodes) {
astNode.accept(this);
}
}
void _visitParameters(FormalParameterList parameters) {
var children = <ComparisonNode>[];
// Note: parameters == null for getters
if (parameters != null) {
for (var parameter in parameters.parameters) {
var element = parameter.declaredElement;
var kind = element.isNotOptional
? 'Required'
: element.isOptionalPositional ? 'Optional' : 'Named';
var parameterChildren = <ComparisonNode>[];
parameterChildren.add(_translateType('Type: ', element.type));
children.add(
ComparisonNode.sorted('$kind: ${element.name}', parameterChildren));
}
}
_resultNodes.add(ComparisonNode('Parameters', children));
}
void _visitTypedef(TypeAlias node) {
var children = <ComparisonNode>[];
var visitor = _AnalyzerVisitor(_typeProvider, children);
GenericTypeAliasElement element = node.declaredElement;
visitor._visitTypeParameters(element.typeParameters);
children.add(_translateType('Type: ', element.function.type));
_resultNodes
.add(ComparisonNode.sorted('Typedef ${node.name.name}', children));
}
void _visitTypeParameters(List<TypeParameterElement> typeParameters) {
for (int i = 0; i < typeParameters.length; i++) {
_resultNodes.add(ComparisonNode(
'Type parameter $i: ${typeParameters[i].name}', [
_translateType(
'Bound: ', typeParameters[i].bound ?? _typeProvider.objectType)
]));
}
}
}

View file

@ -1,138 +0,0 @@
// Copyright (c) 2018, 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 'dart:math';
/// [ComparisonNode] defines a simple tree structure that can be used to compare
/// two representations of Dart code.
///
/// Each node contains a textual string and a list of child nodes.
class ComparisonNode {
final String text;
final List<ComparisonNode> children;
ComparisonNode(this.text, [List<ComparisonNode> children])
: children = children ?? <ComparisonNode>[];
factory ComparisonNode.sorted(
String text, Iterable<ComparisonNode> children) =>
ComparisonNode(text, sortList(children));
@override
bool operator ==(Object other) {
if (other is ComparisonNode) {
if (text != other.text) return false;
if (children.length != other.children.length) return false;
for (int i = 0; i < children.length; i++) {
if (children[i] != other.children[i]) return false;
}
return true;
}
return false;
}
String toString({String newline: '\n'}) {
var lines = ['$text'];
var indentedNewline = '$newline ';
for (var child in children) {
lines.add(child.toString(newline: indentedNewline));
}
return lines.join(indentedNewline);
}
static ComparisonNode diff(
ComparisonNode a, ComparisonNode b, String aName, String bName) {
if (a.text == b.text) {
return ComparisonNode(
a.text, diffLists(a.children, b.children, aName, bName));
} else {
return ComparisonNode('Root nodes differ',
[_prefix('In $aName: ', a), _prefix('In $bName: ', b)]);
}
}
static List<ComparisonNode> diffLists(List<ComparisonNode> a,
List<ComparisonNode> b, String aName, String bName) {
// Note: this is an O(n) "poor man's" diff algorithm; it produces optimal
// results if the incoming results are sorted by text or if there is just
// one contiguous hunk of differences. Otherwise it may not find the
// shortest diff. This should be sufficient for our purposes, since we are
// not expecting many diffs.
// We'll exclude common nodes at the beginning of both lists
var shorterLength = min(a.length, b.length);
var commonInitialNodes = 0;
while (commonInitialNodes < shorterLength &&
a[commonInitialNodes] == b[commonInitialNodes]) {
commonInitialNodes++;
}
// Fast exit if a == b
if (commonInitialNodes == a.length && a.length == b.length) {
return [];
}
// We'll exclude common nodes at the end of both lists (note: we don't want
// to overcount by re-testing the common nodes identified above)
var commonFinalNodes = 0;
while (commonInitialNodes + commonFinalNodes < shorterLength &&
a[a.length - commonFinalNodes - 1] ==
b[b.length - commonFinalNodes - 1]) {
commonFinalNodes++;
}
// Walk the remaining nodes starting at the first node that's different,
// matching up nodes by their text.
var aIndex = commonInitialNodes;
var bIndex = commonInitialNodes;
var aEnd = a.length - commonFinalNodes;
var bEnd = b.length - commonFinalNodes;
var result = <ComparisonNode>[];
while (aIndex < aEnd && bIndex < bEnd) {
var comparisonResult = a[aIndex].text.compareTo(b[bIndex].text);
if (comparisonResult < 0) {
// a[aIndex].text sorts before b[bIndex].text. Assume that this means
// a[aIndex] was removed.
result.add(_prefix('Only in $aName: ', a[aIndex++]));
} else if (comparisonResult > 0) {
// b[bIndex].text sorts before a[aIndex].text. Assume that this means
// b[bIndex] was added.
result.add(_prefix('Only in $bName: ', b[bIndex++]));
} else {
// a[aIndex].text matches b[bIndex].text, so diff the nodes if
// necessary.
var aNode = a[aIndex++];
var bNode = b[bIndex++];
if (aNode != bNode) {
result.add(diff(aNode, bNode, aName, bName));
}
}
}
// Deal with any nodes left over.
while (aIndex < aEnd) {
result.add(_prefix('Only in $aName: ', a[aIndex++]));
}
while (bIndex < bEnd) {
result.add(_prefix('Only in $bName: ', b[bIndex++]));
}
// If we get here and we haven't added any nodes, something has gone wrong.
if (result.isEmpty) {
throw StateError('Diff produced empty diff for non-matching lists');
}
return result;
}
static List<ComparisonNode> sortList(Iterable<ComparisonNode> nodes) {
var result = nodes.toList();
result.sort((a, b) => a.text.compareTo(b.text));
return result;
}
static ComparisonNode _prefix(String prefixString, ComparisonNode node) {
return ComparisonNode(prefixString + node.text, node.children);
}
}

View file

@ -1,360 +0,0 @@
// Copyright (c) 2018, 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 'dart:async';
import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
show DiagnosticMessage, DiagnosticMessageHandler, getMessageHeaderText;
import 'package:analyzer_fe_comparison/src/comparison_node.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show CompilerOptions;
import 'package:front_end/src/api_prototype/kernel_generator.dart';
import 'package:front_end/src/api_prototype/standard_file_system.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/target/targets.dart';
/// Compiles the given [inputs] to kernel using the front_end, and returns a
/// [ComparisonNode] representing them.
Future<ComparisonNode> analyzePackage(
List<Uri> inputs, Uri packagesFileUri, Uri platformUri) async {
var messages = <DiagnosticMessage>[];
var component = (await kernelForModule(inputs,
_makeCompilerOptions(packagesFileUri, platformUri, messages.add)))
.component;
if (messages.isNotEmpty) {
return ComparisonNode(
'Error occurred', messages.map(_diagnosticMessageToNode).toList());
}
var libraryNodes = <ComparisonNode>[];
var visitor = _KernelVisitor(libraryNodes);
for (var library in component.libraries) {
if (inputs.contains(library.importUri)) {
library.accept(visitor);
}
}
return ComparisonNode.sorted('Component', libraryNodes);
}
/// Compiles the given [input] to kernel using the front_end, and returns a
/// [ComparisonNode] representing it.
///
/// Only libraries whose URI passes the [uriFilter] are included in the results.
Future<ComparisonNode> analyzeProgram(Uri input, Uri packagesFileUri,
Uri platformUri, bool uriFilter(Uri uri)) async {
var messages = <DiagnosticMessage>[];
var component = (await kernelForProgram(input,
_makeCompilerOptions(packagesFileUri, platformUri, messages.add)))
.component;
if (messages.isNotEmpty) {
return ComparisonNode(
'Error occurred', messages.map(_diagnosticMessageToNode).toList());
}
var libraryNodes = <ComparisonNode>[];
var visitor = _KernelVisitor(libraryNodes);
for (var library in component.libraries) {
if (uriFilter(library.importUri)) {
library.accept(visitor);
}
}
return ComparisonNode.sorted('Component', libraryNodes);
}
ComparisonNode _diagnosticMessageToNode(DiagnosticMessage message) {
return ComparisonNode(getMessageHeaderText(message));
}
CompilerOptions _makeCompilerOptions(Uri packagesFileUri, Uri platformUri,
DiagnosticMessageHandler onDiagnostic) {
var targetFlags = TargetFlags();
var target = NoneTarget(targetFlags);
var fileSystem = StandardFileSystem.instance;
return CompilerOptions()
..fileSystem = fileSystem
..packagesFileUri = packagesFileUri
..sdkSummary = platformUri
..target = target
..throwOnErrorsForDebugging = false
..embedSourceText = false
..onDiagnostic = onDiagnostic;
}
/// Visitor for serializing a kernel representation of a program into
/// ComparisonNodes.
///
/// Results are accumulated into [_resultNodes].
class _KernelVisitor extends TreeVisitor<void> {
final List<ComparisonNode> _resultNodes;
_KernelVisitor(this._resultNodes);
@override
void defaultTreeNode(TreeNode node) {
throw new UnimplementedError('KernelVisitor: ${node.runtimeType}');
}
@override
void visitClass(Class class_) {
if (class_.isAnonymousMixin) return null;
var kind = class_.isEnum
? 'Enum'
: class_.isMixinApplication
? 'MixinApplication'
: class_.isMixinDeclaration ? 'Mixin' : 'Class';
var children = <ComparisonNode>[];
var visitor = _KernelVisitor(children);
if (class_.isEnum) {
for (var field in class_.fields) {
if (!field.isStatic) continue;
if (field.name.name == 'values') continue;
// TODO(paulberry): handle index
children.add(ComparisonNode('EnumValue ${field.name.name}'));
}
} else {
visitor._visitTypeParameters(class_.typeParameters);
if (class_.supertype != null) {
var declaredSupertype = class_.supertype.asInterfaceType;
if (class_.isMixinDeclaration) {
var constraints = <DartType>[];
// Kernel represents a mixin declaration such as:
// mixin M on S0, S1, S2 {...}
// By desugaring it to:
// abstract class _M&S0&S1 implements S0, S1 {}
// abstract class _M&S0&S1&S2 implements _M&S0&S1 {}
// abstract class M extends M&S0&S1&S2 {...}
// (See dartbug.com/34783)
while (declaredSupertype.classNode.isAnonymousMixin) {
// Since we're walking up the class hierarchy, we encounter the
// mixins in the reverse order that they were declared, so we have
// to use [List.insert] to add them to [constraints].
constraints.insert(
0,
declaredSupertype
.classNode.implementedTypes[1].asInterfaceType);
declaredSupertype =
declaredSupertype.classNode.implementedTypes[0].asInterfaceType;
}
constraints.insert(0, declaredSupertype);
for (int i = 0; i < constraints.length; i++) {
children.add(_TypeVisitor.translate('On $i: ', constraints[i]));
}
} else {
var mixedInTypes = <DartType>[];
if (class_.isMixinApplication) {
mixedInTypes.add(class_.mixedInType.asInterfaceType);
}
while (declaredSupertype.classNode.isAnonymousMixin) {
// Since we're walking from the class to its declared supertype, we
// encounter the mixins in the reverse order that they were declared,
// so we have to use [List.insert] to add them to [mixedInTypes].
mixedInTypes.insert(
0, declaredSupertype.classNode.mixedInType.asInterfaceType);
declaredSupertype =
declaredSupertype.classNode.supertype.asInterfaceType;
}
children.add(_TypeVisitor.translate('Extends: ', declaredSupertype));
for (int i = 0; i < mixedInTypes.length; i++) {
children.add(_TypeVisitor.translate('Mixin $i: ', mixedInTypes[i]));
}
}
}
for (int i = 0; i < class_.implementedTypes.length; i++) {
children.add(_TypeVisitor.translate(
'Implements $i: ', class_.implementedTypes[i].asInterfaceType));
}
visitor._visitList(class_.fields);
visitor._visitList(class_.constructors);
visitor._visitList(class_.procedures);
}
// TODO(paulberry): handle more fields from Class
_resultNodes.add(ComparisonNode.sorted('$kind ${class_.name}', children));
}
@override
void visitConstructor(Constructor constructor) {
if (constructor.isSynthetic) return null;
var name = constructor.name.name;
if (name.isEmpty) {
name = '(unnamed)';
}
var children = <ComparisonNode>[];
var visitor = _KernelVisitor(children);
constructor.function.accept(visitor);
// TODO(paulberry): handle more fields from Constructor
_resultNodes.add(ComparisonNode.sorted('Constructor $name', children));
}
@override
void visitField(Field field) {
if (field.name.name == '_redirecting#') return null;
if (field.name.name == '_exports#') return null;
var children = <ComparisonNode>[];
children.add(_TypeVisitor.translate('Type: ', field.type));
// TODO(paulberry): handle more fields from Field
_resultNodes
.add(ComparisonNode.sorted('Field ${field.name.name}', children));
}
@override
void visitFunctionNode(FunctionNode node) {
var parent = node.parent;
if (!(parent is Constructor || parent is Procedure && parent.isFactory)) {
_visitTypeParameters(node.typeParameters);
_resultNodes
.add(_TypeVisitor.translate('Return type: ', node.returnType));
}
var parameterChildren = <ComparisonNode>[];
var parameterVisitor = _KernelVisitor(parameterChildren);
for (int i = 0; i < node.positionalParameters.length; i++) {
parameterVisitor._visitParameter(node.positionalParameters[i],
i < node.requiredParameterCount ? 'Required' : 'Optional');
}
for (int i = 0; i < node.namedParameters.length; i++) {
parameterVisitor._visitParameter(node.namedParameters[i], 'Named');
}
_resultNodes.add(ComparisonNode('Parameters', parameterChildren));
// TODO(paulberry): handle more fields from FunctionNode
}
@override
void visitLibrary(Library library) {
var children = <ComparisonNode>[];
if (library.name != null) {
children.add(ComparisonNode('name=${library.name}'));
}
var visitor = _KernelVisitor(children);
visitor._visitList(library.typedefs);
visitor._visitList(library.classes);
visitor._visitList(library.procedures);
visitor._visitList(library.fields);
// TODO(paulberry): handle more fields from Library
_resultNodes
.add(ComparisonNode.sorted(library.importUri.toString(), children));
}
@override
void visitProcedure(Procedure procedure) {
if (procedure.isSyntheticForwarder) {
return null;
}
if (procedure.name.name.startsWith('_#loadLibrary_')) {
// Sometimes the front end generates procedures with this name that don't
// correspond to anything in the source file. Ignore them.
return null;
}
// TODO(paulberry): add an annotation to the ComparisonNode when the
// procedure is a factory.
var kind = procedure.isFactory
? 'Constructor'
: procedure.kind.toString().replaceAll('ProcedureKind.', '');
var name = procedure.name.name;
if (name.isEmpty) {
name = '(unnamed)';
}
var children = <ComparisonNode>[];
var visitor = _KernelVisitor(children);
procedure.function.accept(visitor);
// TODO(paulberry): handle more fields from Procedure
_resultNodes.add(ComparisonNode.sorted('$kind $name', children));
}
@override
void visitTypedef(Typedef typedef) {
var children = <ComparisonNode>[];
var visitor = _KernelVisitor(children);
visitor._visitTypeParameters(typedef.typeParameters);
children.add(_TypeVisitor.translate('Type: ', typedef.type));
// TODO(paulberry): handle more fields from Typedef
_resultNodes
.add(ComparisonNode.sorted('Typedef ${typedef.name}', children));
}
/// Visits all the nodes in [nodes].
void _visitList(List<TreeNode> nodes) {
for (var node in nodes) {
node.accept(this);
}
}
void _visitParameter(VariableDeclaration parameter, String kind) {
var children = <ComparisonNode>[];
children.add(_TypeVisitor.translate('Type: ', parameter.type));
// TODO(paulberry): handle more fields from VariableDeclaration
_resultNodes
.add(ComparisonNode.sorted('$kind: ${parameter.name}', children));
}
void _visitTypeParameters(List<TypeParameter> typeParameters) {
for (int i = 0; i < typeParameters.length; i++) {
_resultNodes.add(ComparisonNode(
'Type parameter $i: ${typeParameters[i].name}',
[_TypeVisitor.translate('Bound: ', typeParameters[i].bound)]));
}
}
}
/// Visitor for serializing a kernel representation of a type into
/// ComparisonNodes.
class _TypeVisitor extends DartTypeVisitor<ComparisonNode> {
/// Text to prepend to the node text.
String _prefix;
_TypeVisitor(this._prefix);
@override
ComparisonNode defaultDartType(DartType node) {
throw new UnimplementedError('_TypeVisitor: ${node.runtimeType}');
}
@override
ComparisonNode visitDynamicType(DynamicType node) {
return ComparisonNode('${_prefix}Dynamic');
}
@override
ComparisonNode visitFunctionType(FunctionType node) {
var children = <ComparisonNode>[];
var visitor = _KernelVisitor(children);
visitor._visitTypeParameters(node.typeParameters);
children.add(translate('Return type: ', node.returnType));
for (int i = 0; i < node.positionalParameters.length; i++) {
var kind = i < node.requiredParameterCount ? 'Required' : 'Optional';
children
.add(translate('$kind parameter $i: ', node.positionalParameters[i]));
}
for (var namedType in node.namedParameters) {
children.add(
translate('Named parameter ${namedType.name}: ', namedType.type));
}
return ComparisonNode.sorted('${_prefix}FunctionType', children);
}
@override
ComparisonNode visitInterfaceType(InterfaceType node) {
var children = <ComparisonNode>[];
children.add(ComparisonNode(
'Library: ${node.classNode.enclosingLibrary.importUri}'));
for (int i = 0; i < node.typeArguments.length; i++) {
children.add(translate('Type arg $i: ', node.typeArguments[i]));
}
return ComparisonNode(
'${_prefix}InterfaceType ${node.classNode.name}', children);
}
@override
ComparisonNode visitTypeParameterType(TypeParameterType node) {
// TODO(paulberry): disambiguate if needed.
return ComparisonNode(
'${_prefix}TypeParameterType: ${node.parameter.name}');
}
@override
ComparisonNode visitVoidType(VoidType node) {
return ComparisonNode('${_prefix}Void');
}
static ComparisonNode translate(String prefix, DartType type) {
return type.accept(new _TypeVisitor(prefix));
}
}

View file

@ -1,9 +0,0 @@
name: analyzer_fe_comparison
publish_to: none
author: Dart Team <misc@dartlang.org>
description: Tool for comparing the behavior of the analyzer and the front end.
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_fe_comparison
environment:
sdk: '>=2.0.0-dev-48.0 <3.0.0'
dependencies:
kernel: ^0.3.4

View file

@ -1,33 +0,0 @@
// Copyright (c) 2018, 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 'dart:async';
import 'dart:io';
import 'package:analyzer_fe_comparison/comparison.dart';
import 'package:path/path.dart' as path;
/// Compares the analyzer and front_end behavior when compiling a package.
///
/// Currently hardcoded to use the package "analyzer".
main() async {
var scriptPath = Platform.script.toFilePath();
var sdkRepoPath =
path.normalize(path.join(path.dirname(scriptPath), '..', '..', '..'));
var buildPath = await _findBuildDir(sdkRepoPath, 'ReleaseX64');
var dillPath = path.join(buildPath, 'vm_platform_strong.dill');
var analyzerLibPath = path.join(sdkRepoPath, 'pkg', 'analyzer', 'lib');
var packagesFilePath = path.join(sdkRepoPath, '.packages');
comparePackages(dillPath, analyzerLibPath, packagesFilePath);
}
Future<String> _findBuildDir(String sdkRepoPath, String targetName) async {
for (var subdirName in ['out', 'xcodebuild']) {
var candidatePath = path.join(sdkRepoPath, subdirName, targetName);
if (await new Directory(candidatePath).exists()) {
return candidatePath;
}
}
throw new StateError('Cannot find build directory');
}