Files
sdk/pkg/kernel/lib/import_table.dart
T
Nate Biggs 52be04e90a [dart2wasm][vm] Fix deferred loading with mixin deduplication.
Now moves mixin applications into their own libraries that can be
imported separately from the rest of the code in the originating
library.

Then adds an import to the new library containing the mixin application
to any libraries that use it.

This ensures the import graph is accurate while only making sure it
remains minimal.

TEST=Added case to pkg/vm testcases.

Bug: https://github.com/dart-lang/sdk/issues/43089
Change-Id: I56b6ea1db8261e0d0508c2d7e62d0c13fbe9678b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/457260
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
2025-11-21 10:29:51 -08:00

180 lines
5.1 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;
}
}
@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 LibraryImportTable(Library lib) {
return new _ImportTableBuilder(lib).build();
}
LibraryImportTable.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;
}
_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;
// 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("/");
}