[cfe] Add DillExportNameSpace

This adds NameSpaceImpl, independent of Scope, and subclasses
LazyNameSpace nd DillExportNameSpaces and uses the latter for
the export scope in DillLibraryBuilder.

Change-Id: I06cee953bdef4e5a725d219c4441cc3e510d5544
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/377020
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/lib/src/base/name_space.dart b/pkg/front_end/lib/src/base/name_space.dart
new file mode 100644
index 0000000..a0b1abe
--- /dev/null
+++ b/pkg/front_end/lib/src/base/name_space.dart
@@ -0,0 +1,333 @@
+// 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});
+}
+
+class NameSpaceImpl implements NameSpace {
+  Map<String, Builder>? _getables;
+  Map<String, MemberBuilder>? _setables;
+  Set<ExtensionBuilder>? _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.fromIterators(
+      _getables?.values.iterator,
+      _setables?.values.iterator,
+      _extensions?.iterator);
+
+  @override
+  NameIterator<Builder> get unfilteredNameIterator =>
+      new ScopeNameIterator.fromIterators(
+          _getables, _setables, _extensions?.iterator);
+}
+
+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 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);
+      }
+    }
+  }
+}
diff --git a/pkg/front_end/lib/src/base/scope.dart b/pkg/front_end/lib/src/base/scope.dart
index a27d016..a6bbf47 100644
--- a/pkg/front_end/lib/src/base/scope.dart
+++ b/pkg/front_end/lib/src/base/scope.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// ignore_for_file: annotate_overrides
-
 library fasta.scope;
 
 import 'package:kernel/ast.dart';
@@ -12,7 +10,6 @@
 
 import '../builder/builder.dart';
 import '../builder/declaration_builders.dart';
-import '../builder/library_builder.dart';
 import '../builder/member_builder.dart';
 import '../builder/metadata_builder.dart';
 import '../builder/name_iterator.dart';
@@ -30,6 +27,7 @@
 import '../source/source_member_builder.dart';
 import '../util/helpers.dart' show DelayedActionPerformer;
 import 'messages.dart';
+import 'name_space.dart';
 import 'uri_offset.dart';
 
 enum ScopeKind {
@@ -243,62 +241,6 @@
   String toString() => "Scope(${kind}, $classNameOrDebugName, ${_local?.keys})";
 }
 
-abstract class NameSpace {
-  void addLocalMember(String name, Builder member, {required bool setter});
-
-  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});
-}
-
 class Scope extends MutableScope
     implements ParentScope, LookupScope, NameSpace {
   /// Indicates whether an attempt to declare new names in this scope should
@@ -349,6 +291,7 @@
   ///
   /// The iterator does _not_ include the members and setters mapped in the
   /// [parent] scope.
+  @override
   Iterator<Builder> get unfilteredIterator {
     return new ScopeIterator(this);
   }
@@ -361,6 +304,7 @@
   ///
   /// Compared to [unfilteredIterator] this iterator also gives access to the
   /// name that the builders are mapped to.
+  @override
   NameIterator get unfilteredNameIterator {
     return new ScopeNameIterator(this);
   }
@@ -373,6 +317,7 @@
   /// declared member is included. If [includeAugmentations] is `true`, both
   /// original and augmenting/patching members are included, otherwise, only
   /// original members are included.
+  @override
   Iterator<T> filteredIterator<T extends Builder>(
       {Builder? parent,
       required bool includeDuplicates,
@@ -394,6 +339,7 @@
   ///
   /// Compared to [filteredIterator] this iterator also gives access to the
   /// name that the builders are mapped to.
+  @override
   NameIterator<T> filteredNameIterator<T extends Builder>(
       {Builder? parent,
       required bool includeDuplicates,
@@ -419,150 +365,6 @@
     });
   }
 
-  /// 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 = {};
-    _local?.forEach((String name, Builder builder) {
-      if (replacementMap.containsKey(builder.parent)) {
-        replacedNames.add(name);
-      }
-    });
-    _setters?.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 = _local?[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 = _setters?[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.)
-            (_local ??= {})[name] = replacementGetterFromSetter;
-          }
-        } else if (existingGetter.parent ==
-            replacementLibraryBuilderFromGetter) {
-          // The existing getter should be replaced.
-          if (replacementGetterFromGetter != null) {
-            // With a new getter.
-            (_local ??= // 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).
-            _local?.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.
-            (_setters ??= // 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.
-            (_setters ??= // 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.
-            _setters?.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);
-      }
-    }
-  }
-
   // Coverage-ignore(suite): Not run.
   Scope copyWithParent(Scope parent, String debugName) {
     return new Scope(
@@ -684,10 +486,12 @@
     return builder ?? _parent?.lookupSetter(name, charOffset, fileUri);
   }
 
+  @override
   Builder? lookupLocalMember(String name, {required bool setter}) {
     return setter ? (_setters?[name]) : (_local?[name]);
   }
 
+  @override
   void addLocalMember(String name, Builder member, {required bool setter}) {
     if (setter) {
       (_setters ??= // Coverage-ignore(suite): Not run.
@@ -697,21 +501,26 @@
     }
   }
 
+  @override
   void forEachLocalMember(void Function(String name, Builder member) f) {
     _local?.forEach(f);
   }
 
+  @override
   void forEachLocalSetter(void Function(String name, MemberBuilder member) f) {
     _setters?.forEach(f);
   }
 
+  @override
   void forEachLocalExtension(void Function(ExtensionBuilder member) f) {
     _extensions?.forEach(f);
   }
 
+  @override
   Iterable<Builder> get localMembers => _local?.values ?? const {};
 
   /// Adds [builder] to the extensions in this scope.
+  @override
   void addExtension(ExtensionBuilder builder) {
     _extensions ??= <ExtensionBuilder>{};
     _extensions!.add(builder);
@@ -1311,6 +1120,13 @@
 
   String? _name;
 
+  ScopeNameIterator.fromIterators(Map<String, Builder>? getables,
+      Map<String, Builder>? setables, Iterator<Builder>? extensions)
+      : localNames = getables?.keys.iterator,
+        setterNames = setables?.keys.iterator,
+        super.fromIterators(
+            getables?.values.iterator, setables?.values.iterator, extensions);
+
   ScopeNameIterator(Scope scope)
       : localNames = scope._local?.keys.iterator,
         setterNames = scope._setters?.keys.iterator,
diff --git a/pkg/front_end/lib/src/builder/declaration_builders.dart b/pkg/front_end/lib/src/builder/declaration_builders.dart
index dcb171f..8eb5aa1 100644
--- a/pkg/front_end/lib/src/builder/declaration_builders.dart
+++ b/pkg/front_end/lib/src/builder/declaration_builders.dart
@@ -10,6 +10,7 @@
 import '../base/loader.dart';
 import '../base/messages.dart';
 import '../base/modifier.dart';
+import '../base/name_space.dart';
 import '../base/problems.dart' show internalProblem, unexpected, unhandled;
 import '../base/scope.dart';
 import '../base/uris.dart';
diff --git a/pkg/front_end/lib/src/builder/library_builder.dart b/pkg/front_end/lib/src/builder/library_builder.dart
index 12037f7..bdf6509 100644
--- a/pkg/front_end/lib/src/builder/library_builder.dart
+++ b/pkg/front_end/lib/src/builder/library_builder.dart
@@ -22,6 +22,7 @@
         templateInternalProblemConstructorNotFound,
         templateInternalProblemNotFoundIn,
         templateInternalProblemPrivateConstructorAccess;
+import '../base/name_space.dart';
 import '../base/problems.dart' show internalProblem;
 import '../base/scope.dart';
 import '../base/uri_offset.dart';
diff --git a/pkg/front_end/lib/src/dill/dill_class_builder.dart b/pkg/front_end/lib/src/dill/dill_class_builder.dart
index 4ea03bf..1843735 100644
--- a/pkg/front_end/lib/src/dill/dill_class_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_class_builder.dart
@@ -9,6 +9,7 @@
 
 import '../base/loader.dart';
 import '../base/modifier.dart' show abstractMask, namedMixinApplicationMask;
+import '../base/name_space.dart';
 import '../base/problems.dart' show unimplemented;
 import '../base/scope.dart';
 import '../builder/builder.dart';
diff --git a/pkg/front_end/lib/src/dill/dill_extension_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
index 0c2d701..53be0ed 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
@@ -4,6 +4,7 @@
 
 import 'package:kernel/ast.dart';
 
+import '../base/name_space.dart';
 import '../base/scope.dart';
 import '../builder/declaration_builders.dart';
 import '../builder/library_builder.dart';
diff --git a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
index 7c7e42a..69516f1 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
@@ -4,6 +4,7 @@
 
 import 'package:kernel/ast.dart';
 
+import '../base/name_space.dart';
 import '../base/scope.dart';
 import '../builder/declaration_builders.dart';
 import '../builder/member_builder.dart';
diff --git a/pkg/front_end/lib/src/dill/dill_library_builder.dart b/pkg/front_end/lib/src/dill/dill_library_builder.dart
index c98fc9d..b510e9b 100644
--- a/pkg/front_end/lib/src/dill/dill_library_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_library_builder.dart
@@ -11,6 +11,7 @@
 import '../base/combinator.dart';
 import '../base/export.dart';
 import '../base/loader.dart';
+import '../base/name_space.dart';
 import '../base/problems.dart' show internalProblem, unhandled;
 import '../base/scope.dart';
 import '../base/uris.dart';
@@ -117,7 +118,7 @@
 class DillLibraryBuilder extends LibraryBuilderImpl {
   late final LazyLibraryScope _scope;
 
-  late final LazyLibraryScope _exportScope;
+  late final DillExportNameSpace _exportScope;
 
   @override
   final Library library;
@@ -144,14 +145,14 @@
 
   DillLibraryBuilder(this.library, this.loader) : super(library.fileUri) {
     _scope = new LazyLibraryScope.top(this);
-    _exportScope = new LazyLibraryScope.top(this);
+    _exportScope = new DillExportNameSpace(this);
   }
 
   @override
   Scope get scope => _scope;
 
   @override
-  Scope get nameSpace => _scope;
+  NameSpace get nameSpace => _scope;
 
   @override
   NameSpace get exportScope => _exportScope;
diff --git a/pkg/front_end/lib/src/source/source_class_builder.dart b/pkg/front_end/lib/src/source/source_class_builder.dart
index 582a4a7..40a9b4e 100644
--- a/pkg/front_end/lib/src/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/source/source_class_builder.dart
@@ -21,6 +21,7 @@
         updateBoundNullabilities;
 import 'package:kernel/type_environment.dart';
 
+import '../base/name_space.dart';
 import '../base/problems.dart' show unexpected, unhandled, unimplemented;
 import '../base/scope.dart';
 import '../builder/augmentation_iterator.dart';
diff --git a/pkg/front_end/lib/src/source/source_extension_builder.dart b/pkg/front_end/lib/src/source/source_extension_builder.dart
index ba59da3..cee56b7 100644
--- a/pkg/front_end/lib/src/source/source_extension_builder.dart
+++ b/pkg/front_end/lib/src/source/source_extension_builder.dart
@@ -5,6 +5,7 @@
 import 'package:kernel/ast.dart';
 
 import '../base/common.dart';
+import '../base/name_space.dart';
 import '../builder/builder.dart';
 import '../builder/declaration_builders.dart';
 import '../builder/library_builder.dart';
diff --git a/pkg/front_end/lib/src/source/source_extension_type_declaration_builder.dart b/pkg/front_end/lib/src/source/source_extension_type_declaration_builder.dart
index 1bcb965..3b686a8 100644
--- a/pkg/front_end/lib/src/source/source_extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/source/source_extension_type_declaration_builder.dart
@@ -10,6 +10,7 @@
 import 'package:kernel/type_environment.dart';
 
 import '../base/messages.dart';
+import '../base/name_space.dart';
 import '../base/problems.dart';
 import '../base/scope.dart';
 import '../builder/augmentation_iterator.dart';
diff --git a/pkg/front_end/lib/src/source/source_library_builder.dart b/pkg/front_end/lib/src/source/source_library_builder.dart
index dca3e00..7e80378 100644
--- a/pkg/front_end/lib/src/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/source/source_library_builder.dart
@@ -30,6 +30,7 @@
 import '../base/export.dart' show Export;
 import '../base/import.dart' show Import;
 import '../base/messages.dart';
+import '../base/name_space.dart';
 import '../base/nnbd_mode.dart';
 import '../base/problems.dart' show unexpected, unhandled;
 import '../base/scope.dart';
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index c0d1a9a..de1066a 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -145,7 +145,7 @@
   ),
   // 100.0%.
   "package:front_end/src/base/incremental_compiler.dart": (
-    hitCount: 862,
+    hitCount: 860,
     missCount: 0,
   ),
   // 100.0%.
@@ -178,6 +178,11 @@
     hitCount: 29,
     missCount: 0,
   ),
+  // 66.94915254237289%.
+  "package:front_end/src/base/name_space.dart": (
+    hitCount: 79,
+    missCount: 39,
+  ),
   // 100.0%.
   "package:front_end/src/base/operator.dart": (
     hitCount: 4,
@@ -193,9 +198,9 @@
     hitCount: 246,
     missCount: 0,
   ),
-  // 96.57282741738065%.
+  // 96.41025641025641%.
   "package:front_end/src/base/scope.dart": (
-    hitCount: 789,
+    hitCount: 752,
     missCount: 28,
   ),
   // 100.0%.
@@ -305,7 +310,7 @@
   ),
   // 100.0%.
   "package:front_end/src/builder/library_builder.dart": (
-    hitCount: 63,
+    hitCount: 61,
     missCount: 0,
   ),
   // 100.0%.
@@ -435,7 +440,7 @@
   ),
   // 100.0%.
   "package:front_end/src/dill/dill_library_builder.dart": (
-    hitCount: 338,
+    hitCount: 341,
     missCount: 0,
   ),
   // 100.0%.
@@ -608,9 +613,9 @@
     hitCount: 285,
     missCount: 0,
   ),
-  // 99.90662931839402%.
+  // 99.90645463049579%.
   "package:front_end/src/kernel/kernel_target.dart": (
-    hitCount: 1070,
+    hitCount: 1068,
     missCount: 1,
   ),
   // 100.0%.