blob: 9c81bc02ba3cfc18a80eda0e122c8fd08f38f0ee [file]
// Copyright (c) 2019, 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/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/combinator.dart';
import 'package:analyzer/src/summary2/library_builder.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
class Export {
final LibraryBuilder exporter;
final ExportLocation location;
final List<Combinator> combinators;
Export({
required this.exporter,
required this.location,
required this.combinators,
});
bool addToExportScope(ExportEntry entry) {
if (combinators.allows(entry.name)) {
return exporter.exportScope.export(location, entry);
}
return false;
}
}
final class ExportEntry {
final String name;
final ExportableReference reference;
final List<ExportLocation> locations;
ExportEntry({
required this.name,
required this.reference,
required this.locations,
});
bool get isDeclared => locations.isEmpty;
bool get isReExported => locations.isNotEmpty;
void addLocation(ExportLocation location) {
// This list is very small, contains on it is probably ok.
if (!locations.contains(location)) {
locations.add(location);
}
}
/// We are done updating this object, returns the immutable version.
ExportEntry toFinalized() {
if (isDeclared) {
return this;
}
return ExportEntry(
name: name,
reference: reference,
locations: locations.toFixedList(),
);
}
@override
String toString() {
return '$reference';
}
}
class ExportLocation {
/// The index of the fragment with the `export` directive, `0` means the
/// library file, a positive value means an included fragment.
final int fragmentIndex;
/// The index in [LibraryFragmentImpl.libraryExports].
final int exportIndex;
ExportLocation({required this.fragmentIndex, required this.exportIndex});
@override
bool operator ==(Object other) {
return other is ExportLocation &&
other.fragmentIndex == fragmentIndex &&
other.exportIndex == exportIndex;
}
@override
String toString() {
return '($fragmentIndex, $exportIndex)';
}
}
class ExportScope {
final Map<String, ExportEntry> entriesByName = {};
void declare(String name, ExportableReference reference) {
entriesByName[name] = ExportEntry(
name: name,
reference: reference,
locations: const [],
);
}
bool export(ExportLocation location, ExportEntry entry) {
var name = entry.name;
var existing = entriesByName[name];
if (existing?.reference == entry.reference) {
if (existing != null && existing.isReExported) {
existing.addLocation(location);
}
return false;
}
// Ambiguous declaration detected.
if (existing != null) return false;
entriesByName[name] = ExportEntry(
name: name,
reference: entry.reference,
locations: [location],
);
return true;
}
void forEach(void Function(ExportEntry entry) f) {
entriesByName.forEach((_, entry) {
f(entry);
});
}
List<ExportEntry> toExportEntries() {
return entriesByName.values.map((entry) {
return entry.toFinalized();
}).toFixedList();
}
}