| // 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>{}; | |
| new(Component component) { | |
| for (int i = 0; i < component.libraries.length; ++i) { | |
| _libraryIndex[component.libraries[i]] = i; | |
| } | |
| } | |
| @override | |
| 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(Library lib) { | |
| return new _ImportTableBuilder(lib).build(); | |
| } | |
| new empty(); | |
| 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. | |
| @override | |
| 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; | |
| } | |
| new(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; | |
| // 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.hasScheme && !uri.isScheme('package') && !uri.isScheme('dart'); | |
| bool isTargetSchemeFileOrCustom = isFileOrCustomScheme(targetUri); | |
| bool isReferenceSchemeFileOrCustom = isFileOrCustomScheme(referenceUri); | |
| if (isTargetSchemeFileOrCustom && isReferenceSchemeFileOrCustom) { | |
| String relativeUri = relativeUriPath(targetUri, referenceUri); | |
| table.addImport(target, relativeUri); | |
| } else { | |
| table.addImport(target, target.importUri.toString()); | |
| } | |
| } | |
| @override | |
| void visitClassReference(Class node) { | |
| addLibraryImport(node.enclosingLibrary); | |
| } | |
| @override | |
| void visitLibrary(Library node) { | |
| super.visitLibrary(node); | |
| for (Reference exportedReference in node.additionalExports) { | |
| addLibraryImport(exportedReference.node!.parent as Library); | |
| } | |
| } | |
| @override | |
| void defaultMemberReference(Member node) { | |
| addLibraryImport(node.enclosingLibrary); | |
| } | |
| @override | |
| void 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("/"); | |
| } |