Wire up placeholder executable in pkg/analyzer_fe_comparison.
The placeholder executable is replaced by a shell script that invokes
pkg/analyzer_fe_comparison (in a similar way to how
sdk/bin/dartanalyzer invokes pkg/analyzer_cli). This required some
rewriting of pkg/analyzer_fe_comparison to support analysis of a
single test case (as opposed to analysis of a package, which is what
it did before).
It's now possible to run the comparison tool over the test suite in
language_2. There are many failures, which I'll begin addressing in
follow-up CLs.
Change-Id: I52d8f3b53064e8e29028571d416f2573c6cba35d
Reviewed-on: https://dart-review.googlesource.com/74820
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer_fe_comparison/bin/compare_programs.dart b/pkg/analyzer_fe_comparison/bin/compare_programs.dart
new file mode 100644
index 0000000..0118aee
--- /dev/null
+++ b/pkg/analyzer_fe_comparison/bin/compare_programs.dart
@@ -0,0 +1,48 @@
+// 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);
+}
diff --git a/pkg/analyzer_fe_comparison/bin/compare_sdk_tests b/pkg/analyzer_fe_comparison/bin/compare_sdk_tests
index 96a4e46..b84851f 100755
--- a/pkg/analyzer_fe_comparison/bin/compare_sdk_tests
+++ b/pkg/analyzer_fe_comparison/bin/compare_sdk_tests
@@ -3,5 +3,40 @@
# 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.
-# TODO(paulberry)
-echo "Placeholder text"
+# 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" "$@"
diff --git a/pkg/analyzer_fe_comparison/lib/comparison.dart b/pkg/analyzer_fe_comparison/lib/comparison.dart
index 03138b7..fe3f57e 100644
--- a/pkg/analyzer_fe_comparison/lib/comparison.dart
+++ b/pkg/analyzer_fe_comparison/lib/comparison.dart
@@ -2,15 +2,16 @@
// 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';
+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';
+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 project, and prints
+/// Compares the analyzer and kernel representations of a package, and prints
/// the resulting diff.
-void compare(
+void comparePackages(
String platformPath, String projectLibPath, String packagesFilePath) async {
- ComparisonNode analyzerNode = await driveAnalyzer(projectLibPath);
+ ComparisonNode analyzerNode = await analyzer.analyzePackage(projectLibPath);
var packagesFileUri = Uri.file(packagesFilePath);
var inputs = <Uri>[];
for (var library in analyzerNode.children) {
@@ -18,6 +19,43 @@
}
var platformUri = Uri.file(platformPath);
ComparisonNode kernelNode =
- await driveKernel(inputs, packagesFileUri, platformUri);
+ await kernel.analyzePackage(inputs, packagesFileUri, platformUri);
print(ComparisonNode.diff(kernelNode, analyzerNode));
}
+
+/// 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');
+ 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);
+ var diff = ComparisonNode.diff(kernelNode, analyzerNode);
+ if (diff.children.isEmpty) {
+ print('No differences found!');
+ } else {
+ print('Differences found:');
+ print(diff);
+ }
+}
diff --git a/pkg/analyzer_fe_comparison/lib/src/analyzer.dart b/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
index 207ede3..9da54d4 100644
--- a/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
+++ b/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
@@ -5,6 +5,9 @@
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';
@@ -13,39 +16,76 @@
import 'package:analyzer/src/generated/source.dart' show SourceKind;
import 'package:analyzer_fe_comparison/src/comparison_node.dart';
-/// Analyzes the project located at [libPath] using the analyzer, and returns a
+/// 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> driveAnalyzer(String libPath) async {
- var contextCollection = AnalysisContextCollection(includedPaths: [libPath]);
- 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;
- var libraryNodes = <ComparisonNode>[];
- for (var filePath in contextRoot.analyzedFiles()) {
- 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}'));
+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));
}
- 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);
}
- 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
diff --git a/pkg/analyzer_fe_comparison/lib/src/kernel.dart b/pkg/analyzer_fe_comparison/lib/src/kernel.dart
index 935dd2c..d9d8c89 100644
--- a/pkg/analyzer_fe_comparison/lib/src/kernel.dart
+++ b/pkg/analyzer_fe_comparison/lib/src/kernel.dart
@@ -13,22 +13,10 @@
/// Compiles the given [inputs] to kernel using the front_end, and returns a
/// [ComparisonNode] representing them.
-Future<ComparisonNode> driveKernel(
+Future<ComparisonNode> analyzePackage(
List<Uri> inputs, Uri packagesFileUri, Uri platformUri) async {
- var targetFlags = TargetFlags(strongMode: true, syncAsync: true);
- var target = NoneTarget(targetFlags);
- var fileSystem = StandardFileSystem.instance;
-
- var compilerOptions = CompilerOptions()
- ..fileSystem = fileSystem
- ..packagesFileUri = packagesFileUri
- ..sdkSummary = platformUri
- ..strongMode = true
- ..target = target
- ..throwOnErrorsForDebugging = true
- ..embedSourceText = false;
-
- var component = await kernelForComponent(inputs, compilerOptions);
+ var component = await kernelForComponent(
+ inputs, _makeCompilerOptions(packagesFileUri, platformUri));
var libraryNodes = <ComparisonNode>[];
var visitor = _KernelVisitor(libraryNodes);
for (var library in component.libraries) {
@@ -39,6 +27,39 @@
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 component = await kernelForProgram(
+ input, _makeCompilerOptions(packagesFileUri, platformUri));
+ 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);
+}
+
+CompilerOptions _makeCompilerOptions(Uri packagesFileUri, Uri platformUri) {
+ var targetFlags = TargetFlags(strongMode: true, syncAsync: true);
+ var target = NoneTarget(targetFlags);
+ var fileSystem = StandardFileSystem.instance;
+
+ return CompilerOptions()
+ ..fileSystem = fileSystem
+ ..packagesFileUri = packagesFileUri
+ ..sdkSummary = platformUri
+ ..strongMode = true
+ ..target = target
+ ..throwOnErrorsForDebugging = false
+ ..embedSourceText = false;
+}
+
/// Visitor for serializing a kernel representation of a program into
/// ComparisonNodes.
///
diff --git a/pkg/analyzer_fe_comparison/tool/compare_packages.dart b/pkg/analyzer_fe_comparison/tool/compare_packages.dart
index efb0efe..e1a7b71 100644
--- a/pkg/analyzer_fe_comparison/tool/compare_packages.dart
+++ b/pkg/analyzer_fe_comparison/tool/compare_packages.dart
@@ -19,7 +19,7 @@
var dillPath = path.join(buildPath, 'vm_platform_strong.dill');
var analyzerLibPath = path.join(sdkRepoPath, 'pkg', 'analyzer', 'lib');
var packagesFilePath = path.join(sdkRepoPath, '.packages');
- compare(dillPath, analyzerLibPath, packagesFilePath);
+ comparePackages(dillPath, analyzerLibPath, packagesFilePath);
}
Future<String> _findBuildDir(String sdkRepoPath, String targetName) async {
diff --git a/tools/testing/dart/command_output.dart b/tools/testing/dart/command_output.dart
index 7e53d0e..d26a4dd 100644
--- a/tools/testing/dart/command_output.dart
+++ b/tools/testing/dart/command_output.dart
@@ -584,8 +584,12 @@
if (hasTimedOut) return Expectation.timeout;
if (hasNonUtf8) return Expectation.nonUtf8Error;
- // TODO(paulberry): parse command output
- return Expectation.pass;
+ if (exitCode != 0) return Expectation.fail;
+ for (var line in decodeUtf8(this.stdout).split('\n')) {
+ if (line.indexOf('No differences found') != -1) return Expectation.pass;
+ if (line.indexOf('Differences found') != -1) return Expectation.fail;
+ }
+ return Expectation.fail;
}
/// Cloned code from member result(), with changes.
@@ -596,8 +600,12 @@
if (hasTimedOut) return Expectation.timeout;
if (hasNonUtf8) return Expectation.nonUtf8Error;
- // TODO(paulberry): parse command output
- return Expectation.pass;
+ if (exitCode != 0) return Expectation.fail;
+ for (var line in decodeUtf8(this.stdout).split('\n')) {
+ if (line.indexOf('No differences found') != -1) return Expectation.pass;
+ if (line.indexOf('Differences found') != -1) return Expectation.fail;
+ }
+ return Expectation.fail;
}
}