5b5956bd4e
This is a first pass at running inlined tests (seemingly called doctests in for instance python) on internal CFE code. This is not useful for testing the entire compiler for instance, but could be useful for utility methods that take simple inputs and provide simple outputs. I've added tests to a few tings to try it out. The syntax for it here is probably overly verbose but we could iterate on that if need be. The way it's done is that the test-code is extracted from the comments and (virtually) "injected" in a weirdly named top level method of the same file, which is then called from a "fake" main; the whole thing is compiled to dill and run in an isolate. (This is an application where making advanced invalidation support adding top level methods would be great). Change-Id: I7927822bca0ba68780458c40fe585882330e3b60 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203242 Commit-Queue: Jens Johansen <jensj@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
188 lines
5.6 KiB
Dart
188 lines
5.6 KiB
Dart
// Copyright (c) 2016, 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.
|
|
|
|
library kernel.import_table;
|
|
|
|
import 'ast.dart';
|
|
|
|
abstract class ImportTable {
|
|
int getImportIndex(Library library);
|
|
}
|
|
|
|
class ComponentImportTable implements ImportTable {
|
|
final Map<Library, int> _libraryIndex = <Library, int>{};
|
|
|
|
ComponentImportTable(Component component) {
|
|
for (int i = 0; i < component.libraries.length; ++i) {
|
|
_libraryIndex[component.libraries[i]] = i;
|
|
}
|
|
}
|
|
|
|
int getImportIndex(Library library) => _libraryIndex[library] ?? -1;
|
|
}
|
|
|
|
class LibraryImportTable implements ImportTable {
|
|
final List<String> _importPaths = <String>[];
|
|
final List<Library> _importedLibraries = <Library>[];
|
|
final Map<Library, int> _libraryIndex = <Library, int>{};
|
|
|
|
factory LibraryImportTable(Library lib) {
|
|
return new _ImportTableBuilder(lib).build();
|
|
}
|
|
|
|
LibraryImportTable.empty();
|
|
|
|
/// The list of imports.
|
|
///
|
|
/// Should not be modified directly, as the index map would go out of sync.
|
|
List<String> get importPaths => _importPaths;
|
|
|
|
List<Library> get importedLibraries => _importedLibraries;
|
|
|
|
int addImport(Library target, String importPath) {
|
|
int? index = _libraryIndex[target];
|
|
if (index != null) return index;
|
|
index = _importPaths.length;
|
|
_importPaths.add(importPath);
|
|
_importedLibraries.add(target);
|
|
_libraryIndex[target] = index;
|
|
return index;
|
|
}
|
|
|
|
/// Returns the index of the given import, or -1 if not found.
|
|
int getImportIndex(Library library) {
|
|
return _libraryIndex[library] ?? -1;
|
|
}
|
|
|
|
String getImportPath(Library library) {
|
|
return _importPaths[getImportIndex(library)];
|
|
}
|
|
}
|
|
|
|
/// Builds the import table for a given library.
|
|
class _ImportTableBuilder extends RecursiveVisitor {
|
|
final LibraryImportTable table = new LibraryImportTable.empty();
|
|
final Library referenceLibrary;
|
|
|
|
LibraryImportTable build() {
|
|
referenceLibrary.accept(this);
|
|
return table;
|
|
}
|
|
|
|
_ImportTableBuilder(this.referenceLibrary) {
|
|
table.addImport(referenceLibrary, '');
|
|
}
|
|
|
|
void addLibraryImport(Library? target) {
|
|
if (target == referenceLibrary) return; // Self-reference is special.
|
|
if (target == null) return;
|
|
Uri referenceUri = referenceLibrary.importUri;
|
|
Uri? targetUri = target.importUri;
|
|
// ignore: unnecessary_null_comparison
|
|
if (targetUri == null) {
|
|
throw '$referenceUri cannot refer to library without an import URI';
|
|
}
|
|
// To support using custom-uris in unit tests, we don't check directly
|
|
// whether the scheme is 'file:', but instead we check that is not 'dart:'
|
|
// or 'package:'.
|
|
bool isFileOrCustomScheme(Uri uri) =>
|
|
uri.scheme != '' && uri.scheme != 'package' && uri.scheme != 'dart';
|
|
bool isTargetSchemeFileOrCustom = isFileOrCustomScheme(targetUri);
|
|
bool isReferenceSchemeFileOrCustom = isFileOrCustomScheme(referenceUri);
|
|
if (isTargetSchemeFileOrCustom && isReferenceSchemeFileOrCustom) {
|
|
String relativeUri = relativeUriPath(targetUri, referenceUri);
|
|
table.addImport(target, relativeUri);
|
|
} else if (isTargetSchemeFileOrCustom) {
|
|
// Cannot import a file:URI from a dart:URI or package:URI.
|
|
// We may want to remove this restriction, but for now it's just a sanity
|
|
// check.
|
|
throw '$referenceUri cannot refer to application library $targetUri';
|
|
} else {
|
|
table.addImport(target, target.importUri.toString());
|
|
}
|
|
}
|
|
|
|
visitClassReference(Class node) {
|
|
addLibraryImport(node.enclosingLibrary);
|
|
}
|
|
|
|
visitLibrary(Library node) {
|
|
super.visitLibrary(node);
|
|
for (Reference exportedReference in node.additionalExports) {
|
|
addLibraryImport(exportedReference.node!.parent as Library);
|
|
}
|
|
}
|
|
|
|
defaultMemberReference(Member node) {
|
|
addLibraryImport(node.enclosingLibrary);
|
|
}
|
|
|
|
visitName(Name name) {
|
|
if (name.library != null) {
|
|
addLibraryImport(name.library);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// DartDocTest(
|
|
/// relativeUriPath(
|
|
/// Uri.parse("file:///path/to/file1.dart"),
|
|
/// Uri.parse("file:///path/to/file2.dart"),
|
|
/// ),
|
|
/// "file1.dart"
|
|
/// )
|
|
/// DartDocTest(
|
|
/// relativeUriPath(
|
|
/// Uri.parse("file:///path/to/a/file1.dart"),
|
|
/// Uri.parse("file:///path/to/file2.dart"),
|
|
/// ),
|
|
/// "a/file1.dart"
|
|
/// )
|
|
/// DartDocTest(
|
|
/// relativeUriPath(
|
|
/// Uri.parse("file:///path/to/file1.dart"),
|
|
/// Uri.parse("file:///path/to/b/file2.dart"),
|
|
/// ),
|
|
/// "../file1.dart"
|
|
/// )
|
|
/// DartDocTest(
|
|
/// relativeUriPath(
|
|
/// Uri.parse("file:///path/to/file1.dart"),
|
|
/// Uri.parse("file:///different/path/to/file2.dart"),
|
|
/// ),
|
|
/// "../../../path/to/file1.dart"
|
|
/// )
|
|
String relativeUriPath(Uri target, Uri ref) {
|
|
List<String> targetSegments = target.pathSegments;
|
|
List<String> refSegments = ref.pathSegments;
|
|
int to = refSegments.length;
|
|
if (targetSegments.length < to) to = targetSegments.length;
|
|
to--; // The last entry is the filename, here we compare only directories.
|
|
int same = -1;
|
|
for (int i = 0; i < to; i++) {
|
|
if (targetSegments[i] == refSegments[i]) {
|
|
same = i;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (same == targetSegments.length - 2 &&
|
|
targetSegments.length == refSegments.length) {
|
|
// Both parts have the same number of segments,
|
|
// and they agree on all directories.
|
|
if (targetSegments.last == "") return ".";
|
|
return targetSegments.last;
|
|
}
|
|
List<String> path = <String>[];
|
|
int oked = same + 1;
|
|
while (oked < refSegments.length - 1) {
|
|
path.add("..");
|
|
oked++;
|
|
}
|
|
path.addAll(targetSegments.skip(same + 1));
|
|
|
|
if (path.isEmpty) path.add(".");
|
|
return path.join("/");
|
|
}
|