| // 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(); |
| } |
| } |