blob: 03d0f9bfe95f77a50f86154ded3f4d40ed30175e [file] [log] [blame]
// Copyright (c) 2024, 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 '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/name_iterator.dart';
import '../dill/dill_library_builder.dart';
import 'scope.dart';
abstract class NameSpace {
void addLocalMember(String name, Builder member, {required bool setter});
/// Adds [builder] to the extensions in this name space.
void addExtension(ExtensionBuilder builder);
Builder? lookupLocalMember(String name, {required bool setter});
void forEachLocalMember(void Function(String name, Builder member) f);
void forEachLocalSetter(void Function(String name, MemberBuilder member) f);
void forEachLocalExtension(void Function(ExtensionBuilder member) f);
Iterable<Builder> get localMembers;
/// Returns an iterator of all members and setters mapped in this name space,
/// including duplicate members mapped to the same name.
Iterator<Builder> get unfilteredIterator;
/// Returns an iterator of all members and setters mapped in this name space,
/// including duplicate members mapped to the same name.
///
/// Compared to [unfilteredIterator] this iterator also gives access to the
/// name that the builders are mapped to.
NameIterator get unfilteredNameIterator;
/// Returns a filtered iterator of members and setters mapped in this name
/// space.
///
/// Only members of type [T] are included. If [parent] is provided, on members
/// declared in [parent] are included. If [includeDuplicates] is `true`, all
/// duplicates of the same name are included, otherwise, only the first
/// declared member is included. If [includeAugmentations] is `true`, both
/// original and augmenting/patching members are included, otherwise, only
/// original members are included.
Iterator<T> filteredIterator<T extends Builder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations});
/// Returns a filtered iterator of members and setters mapped in this name
/// space.
///
/// Only members of type [T] are included. If [parent] is provided, on members
/// declared in [parent] are included. If [includeDuplicates] is `true`, all
/// duplicates of the same name are included, otherwise, only the first
/// declared member is included. If [includeAugmentations] is `true`, both
/// original and augmenting/patching members are included, otherwise, only
/// original members are included.
///
/// Compared to [filteredIterator] this iterator also gives access to the
/// name that the builders are mapped to.
NameIterator<T> filteredNameIterator<T extends Builder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations});
}
abstract class DeclarationNameSpace implements NameSpace {
MemberBuilder? lookupConstructor(String name);
void addConstructor(String name, MemberBuilder builder);
void forEachConstructor(void Function(String, MemberBuilder) f);
/// Returns an iterator of all constructors mapped in this scope,
/// including duplicate constructors mapped to the same name.
Iterator<MemberBuilder> get unfilteredConstructorIterator;
/// Returns an iterator of all constructors mapped in this scope,
/// including duplicate constructors mapped to the same name.
///
/// Compared to [unfilteredConstructorIterator] this iterator also gives
/// access to the name that the builders are mapped to.
NameIterator<MemberBuilder> get unfilteredConstructorNameIterator;
/// Returns a filtered iterator of constructors mapped in this scope.
///
/// Only members of type [T] are included. If [parent] is provided, on members
/// declared in [parent] are included. If [includeDuplicates] is `true`, all
/// duplicates of the same name are included, otherwise, only the first
/// declared member is included. If [includeAugmentations] is `true`, both
/// original and augmenting/patching members are included, otherwise, only
/// original members are included.
Iterator<T> filteredConstructorIterator<T extends MemberBuilder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations});
/// Returns a filtered iterator of constructors mapped in this scope.
///
/// Only members of type [T] are included. If [parent] is provided, on members
/// declared in [parent] are included. If [includeDuplicates] is `true`, all
/// duplicates of the same name are included, otherwise, only the first
/// declared member is included. If [includeAugmentations] is `true`, both
/// original and augmenting/patching members are included, otherwise, only
/// original members are included.
///
/// Compared to [filteredConstructorIterator] this iterator also gives access
/// to the name that the builders are mapped to.
NameIterator<T> filteredConstructorNameIterator<T extends MemberBuilder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations});
}
class NameSpaceImpl implements NameSpace {
Map<String, Builder>? _getables;
Map<String, MemberBuilder>? _setables;
Set<ExtensionBuilder>? _extensions;
NameSpaceImpl(
{Map<String, Builder>? getables,
Map<String, MemberBuilder>? setables,
Set<ExtensionBuilder>? extensions})
: _getables = getables,
_setables = setables,
_extensions = extensions;
@override
void addLocalMember(String name, Builder member, {required bool setter}) {
if (setter) {
(_setables ??= {})[name] = member as MemberBuilder;
} else {
(_getables ??= {})[name] = member;
}
}
@override
void addExtension(ExtensionBuilder builder) {
(_extensions ??= {}).add(builder);
}
@override
Iterator<T> filteredIterator<T extends Builder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations}) {
return new FilteredIterator<T>(unfilteredIterator,
parent: parent,
includeDuplicates: includeDuplicates,
includeAugmentations: includeAugmentations);
}
@override
NameIterator<T> filteredNameIterator<T extends Builder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations}) {
return new FilteredNameIterator<T>(unfilteredNameIterator,
parent: parent,
includeDuplicates: includeDuplicates,
includeAugmentations: includeAugmentations);
}
@override
void forEachLocalExtension(void Function(ExtensionBuilder member) f) {
_extensions?.forEach(f);
}
@override
void forEachLocalMember(void Function(String name, Builder member) f) {
if (_getables != null) {
for (MapEntry<String, Builder> entry in _getables!.entries) {
f(entry.key, entry.value);
}
}
}
@override
void forEachLocalSetter(void Function(String name, MemberBuilder member) f) {
if (_setables != null) {
for (MapEntry<String, MemberBuilder> entry in _setables!.entries) {
f(entry.key, entry.value);
}
}
}
@override
Iterable<Builder> get localMembers => _getables?.values ?? const [];
@override
Builder? lookupLocalMember(String name, {required bool setter}) {
Map<String, Builder>? map = setter ? _setables : _getables;
return map?[name];
}
@override
Iterator<Builder> get unfilteredIterator => new ScopeIterator(
_getables?.values.iterator,
_setables?.values.iterator,
_extensions?.iterator);
@override
NameIterator<Builder> get unfilteredNameIterator =>
new ScopeNameIterator(_getables, _setables, _extensions?.iterator);
}
class DeclarationNameSpaceImpl extends NameSpaceImpl
implements DeclarationNameSpace {
Map<String, MemberBuilder>? _constructors;
DeclarationNameSpaceImpl(
{super.getables,
super.setables,
super.extensions,
Map<String, MemberBuilder>? constructors})
: _constructors = constructors;
@override
void addConstructor(String name, MemberBuilder builder) {
(_constructors ??= {})[name] = builder;
}
@override
MemberBuilder? lookupConstructor(String name) => _constructors?[name];
/// Returns an iterator of all constructors mapped in this scope,
/// including duplicate constructors mapped to the same name.
@override
Iterator<MemberBuilder> get unfilteredConstructorIterator =>
new ConstructorNameSpaceIterator(_constructors?.values.iterator);
@override
NameIterator<MemberBuilder> get unfilteredConstructorNameIterator =>
new ConstructorNameSpaceNameIterator(
_constructors?.keys.iterator, _constructors?.values.iterator);
@override
Iterator<T> filteredConstructorIterator<T extends MemberBuilder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations}) {
return new FilteredIterator<T>(unfilteredConstructorIterator,
parent: parent,
includeDuplicates: includeDuplicates,
includeAugmentations: includeAugmentations);
}
@override
NameIterator<T> filteredConstructorNameIterator<T extends MemberBuilder>(
{Builder? parent,
required bool includeDuplicates,
required bool includeAugmentations}) {
return new FilteredNameIterator<T>(unfilteredConstructorNameIterator,
parent: parent,
includeDuplicates: includeDuplicates,
includeAugmentations: includeAugmentations);
}
@override
void forEachConstructor(void Function(String, MemberBuilder) f) {
_constructors?.forEach(f);
}
}
abstract class LazyNameSpace extends NameSpaceImpl {
/// Override this method to lazily populate the scope before access.
void ensureNameSpace();
@override
Map<String, Builder>? get _getables {
ensureNameSpace();
return super._getables;
}
@override
Map<String, MemberBuilder>? get _setables {
ensureNameSpace();
return super._setables;
}
@override
Set<ExtensionBuilder>? get _extensions {
ensureNameSpace();
return super._extensions;
}
}
class DillLibraryNameSpace extends LazyNameSpace {
final DillLibraryBuilder _libraryBuilder;
DillLibraryNameSpace(this._libraryBuilder);
@override
void ensureNameSpace() {
_libraryBuilder.ensureLoaded();
}
}
class DillExportNameSpace extends LazyNameSpace {
final DillLibraryBuilder _libraryBuilder;
DillExportNameSpace(this._libraryBuilder);
@override
void ensureNameSpace() {
_libraryBuilder.ensureLoaded();
}
/// Patch up the scope, using the two replacement maps to replace builders in
/// scope. The replacement maps from old LibraryBuilder to map, mapping
/// from name to new (replacement) builder.
void patchUpScope(Map<LibraryBuilder, Map<String, Builder>> replacementMap,
Map<LibraryBuilder, Map<String, Builder>> replacementMapSetters) {
// In the following we refer to non-setters as 'getters' for brevity.
//
// We have to replace all getters and setters in [_locals] and [_setters]
// with the corresponding getters and setters in [replacementMap]
// and [replacementMapSetters].
//
// Since field builders can be replaced by getter and setter builders and
// vice versa when going from source to dill builder and back, we might not
// have a 1-to-1 relationship between the existing and replacing builders.
//
// For this reason we start by collecting the names of all getters/setters
// that need (some) replacement. Afterwards we go through these names
// handling both getters and setters at the same time.
Set<String> replacedNames = {};
_getables?.forEach((String name, Builder builder) {
if (replacementMap.containsKey(builder.parent)) {
replacedNames.add(name);
}
});
_setables?.forEach((String name, Builder builder) {
if (replacementMapSetters.containsKey(builder.parent)) {
replacedNames.add(name);
}
});
if (replacedNames.isNotEmpty) {
for (String name in replacedNames) {
// We start be collecting the relation between an existing getter/setter
// and the getter/setter that will replace it. This information is used
// below to handle all the different cases that can occur.
Builder? existingGetter = _getables?[name];
LibraryBuilder? replacementLibraryBuilderFromGetter;
Builder? replacementGetterFromGetter;
Builder? replacementSetterFromGetter;
if (existingGetter != null &&
replacementMap.containsKey(existingGetter.parent)) {
replacementLibraryBuilderFromGetter =
existingGetter.parent as LibraryBuilder;
replacementGetterFromGetter =
replacementMap[replacementLibraryBuilderFromGetter]![name];
replacementSetterFromGetter =
replacementMapSetters[replacementLibraryBuilderFromGetter]![name];
}
Builder? existingSetter = _setables?[name];
LibraryBuilder? replacementLibraryBuilderFromSetter;
Builder? replacementGetterFromSetter;
Builder? replacementSetterFromSetter;
if (existingSetter != null &&
replacementMap.containsKey(existingSetter.parent)) {
replacementLibraryBuilderFromSetter =
existingSetter.parent as LibraryBuilder;
replacementGetterFromSetter =
replacementMap[replacementLibraryBuilderFromSetter]![name];
replacementSetterFromSetter =
replacementMapSetters[replacementLibraryBuilderFromSetter]![name];
}
if (existingGetter == null) {
// Coverage-ignore-block(suite): Not run.
// No existing getter.
if (replacementGetterFromSetter != null) {
// We might have had one implicitly from the setter. Use it here,
// if so. (This is currently not possible, but added to match the
// case for setters below.)
(_getables ??= {})[name] = replacementGetterFromSetter;
}
} else if (existingGetter.parent ==
replacementLibraryBuilderFromGetter) {
// The existing getter should be replaced.
if (replacementGetterFromGetter != null) {
// With a new getter.
(_getables ??= // Coverage-ignore(suite): Not run.
{})[name] = replacementGetterFromGetter;
} else {
// Coverage-ignore-block(suite): Not run.
// With `null`, i.e. removed. This means that the getter is
// implicitly available through the setter. (This is currently not
// possible, but handled here to match the case for setters below).
_getables?.remove(name);
}
} else {
// Leave the getter in - it wasn't replaced.
}
if (existingSetter == null) {
// No existing setter.
if (replacementSetterFromGetter != null) {
// We might have had one implicitly from the getter. Use it here,
// if so.
(_setables ??= // Coverage-ignore(suite): Not run.
{})[name] = replacementSetterFromGetter as MemberBuilder;
}
} else if (existingSetter.parent ==
replacementLibraryBuilderFromSetter) {
// The existing setter should be replaced.
if (replacementSetterFromSetter != null) {
// With a new setter.
(_setables ??= // Coverage-ignore(suite): Not run.
{})[name] = replacementSetterFromSetter as MemberBuilder;
} else {
// With `null`, i.e. removed. This means that the setter is
// implicitly available through the getter. This happens when the
// getter is a field builder for an assignable field.
_setables?.remove(name);
}
} else {
// Leave the setter in - it wasn't replaced.
}
}
}
if (_extensions != null) {
// Coverage-ignore-block(suite): Not run.
bool needsPatching = false;
for (ExtensionBuilder extensionBuilder in _extensions!) {
if (replacementMap.containsKey(extensionBuilder.parent)) {
needsPatching = true;
break;
}
}
if (needsPatching) {
Set<ExtensionBuilder> extensionsReplacement =
new Set<ExtensionBuilder>();
for (ExtensionBuilder extensionBuilder in _extensions!) {
if (replacementMap.containsKey(extensionBuilder.parent)) {
assert(replacementMap[extensionBuilder.parent]![
extensionBuilder.name] !=
null);
extensionsReplacement.add(
replacementMap[extensionBuilder.parent]![extensionBuilder.name]
as ExtensionBuilder);
break;
} else {
extensionsReplacement.add(extensionBuilder);
}
}
_extensions!.clear();
extensionsReplacement.addAll(extensionsReplacement);
}
}
}
}
class PrefixNameSpace extends NameSpaceImpl {
void merge(
PrefixNameSpace nameSpace,
Builder computeAmbiguousDeclaration(
String name, Builder existing, Builder member)) {
Map<String, Builder> map = const {};
void mergeMember(String name, Builder member) {
Builder? existing = map[name];
if (existing != null) {
if (existing != member) {
member = computeAmbiguousDeclaration(name, existing, member);
}
}
map[name] = member;
}
if (nameSpace._getables != null) {
map = _getables ??= // Coverage-ignore(suite): Not run.
{};
nameSpace._getables?.forEach(mergeMember);
}
if (nameSpace._setables != null) {
// Coverage-ignore-block(suite): Not run.
map = _setables ??= {};
nameSpace._setables?.forEach(mergeMember);
}
if (nameSpace._extensions != null) {
(_extensions ??= {}).addAll(nameSpace._extensions!);
}
}
}