mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:59:47 +00:00
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:
parent
92b1057f3f
commit
4b1fd4f670
|
@ -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);
|
|
||||||
}
|
|
|
@ -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" "$@"
|
|
|
@ -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'));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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');
|
|
||||||
}
|
|
Loading…
Reference in a new issue