[cfe] Separate patch from regular libraries

+ handle them through the origin library.

This prepares for supporting multiple patch libraries for the same
library.

Change-Id: Ib4934fd2f2c7f743fa342dae86002ac72d57f9a3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/222304
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/builder.dart b/pkg/front_end/lib/src/fasta/builder/builder.dart
index f933d9f..7b208f1 100644
--- a/pkg/front_end/lib/src/fasta/builder/builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/builder.dart
@@ -200,10 +200,6 @@
   /// Returns the number of patches that was finished.
   int finishPatch();
 
-  /// Resolve constructors (lookup names in scope) recorded in this builder and
-  /// return the number of constructors resolved.
-  int resolveConstructors(covariant Builder? parent);
-
   /// Return `true` if this builder is a duplicate of another with the same
   /// name. This is `false` for the builder first declared amongst duplicates.
   bool get isDuplicate;
@@ -305,8 +301,5 @@
   }
 
   @override
-  int resolveConstructors(covariant Builder parent) => 0;
-
-  @override
   bool get isDuplicate => next != null;
 }
diff --git a/pkg/front_end/lib/src/fasta/builder/library_builder.dart b/pkg/front_end/lib/src/fasta/builder/library_builder.dart
index 1672a7d..f2c63b8 100644
--- a/pkg/front_end/lib/src/fasta/builder/library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/library_builder.dart
@@ -29,7 +29,6 @@
 
 import 'builder.dart';
 import 'class_builder.dart';
-import 'field_builder.dart';
 import 'member_builder.dart';
 import 'modifier_builder.dart';
 import 'name_iterator.dart';
@@ -101,14 +100,6 @@
       String name, Builder declaration, Builder other, int charOffset,
       {bool isExport: false, bool isImport: false});
 
-  int finishDeferredLoadTearoffs();
-
-  int finishForwarders();
-
-  int finishNativeMethods();
-
-  int finishPatchMethods();
-
   /// Looks up [constructorName] in the class named [className].
   ///
   /// The class is looked up in this library's export scope unless
@@ -124,21 +115,6 @@
   MemberBuilder getConstructor(String className,
       {String constructorName, bool bypassLibraryPrivacy: false});
 
-  int finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType);
-
-  /// Computes variances of type parameters on typedefs.
-  ///
-  /// The variance property of type parameters on typedefs is computed from the
-  /// use of the parameters in the right-hand side of the typedef definition.
-  int computeVariances() => 0;
-
-  /// This method instantiates type parameters to their bounds in some cases
-  /// where they were omitted by the programmer and not provided by the type
-  /// inference.  The method returns the number of distinct type variables
-  /// that were instantiated in this library.
-  int computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType,
-      TypeBuilder bottomType, ClassBuilder objectClass);
-
   void becomeCoreLibrary();
 
   void addSyntheticDeclarationOfDynamic();
@@ -160,10 +136,6 @@
 
   void recordAccess(int charOffset, int length, Uri fileUri);
 
-  void buildOutlineExpressions();
-
-  List<FieldBuilder>? takeImplicitlyTypedFields();
-
   bool get isNonNullableByDefault;
 
   Nullability get nullable;
@@ -286,18 +258,6 @@
   }
 
   @override
-  int finishDeferredLoadTearoffs() => 0;
-
-  @override
-  int finishForwarders() => 0;
-
-  @override
-  int finishNativeMethods() => 0;
-
-  @override
-  int finishPatchMethods() => 0;
-
-  @override
   MemberBuilder getConstructor(String className,
       {String? constructorName, bool bypassLibraryPrivacy: false}) {
     constructorName ??= "";
@@ -340,18 +300,6 @@
   }
 
   @override
-  int finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) => 0;
-
-  @override
-  int computeVariances() => 0;
-
-  @override
-  int computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType,
-      TypeBuilder bottomType, ClassBuilder objectClass) {
-    return 0;
-  }
-
-  @override
   void becomeCoreLibrary() {
     if (scope.lookupLocalMember("dynamic", setter: false) == null) {
       addSyntheticDeclarationOfDynamic();
@@ -392,12 +340,6 @@
   void recordAccess(int charOffset, int length, Uri fileUri) {}
 
   @override
-  void buildOutlineExpressions() {}
-
-  @override
-  List<FieldBuilder>? takeImplicitlyTypedFields() => null;
-
-  @override
   Nullability get nullable {
     return isNonNullableByDefault ? Nullability.nullable : Nullability.legacy;
   }
@@ -438,7 +380,7 @@
 
   @override
   StringBuffer printOn(StringBuffer buffer) {
-    return buffer..write(name ?? importUri);
+    return buffer..write(name ?? (isPart ? fileUri : importUri));
   }
 }
 
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index c693edd..a95dd25 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -465,24 +465,17 @@
     Set<Library> newDillLibraryBuilders = new Set<Library>();
     _userBuilders ??= <Uri, LibraryBuilder>{};
     Map<LibraryBuilder, List<LibraryBuilder>>? convertedLibraries;
-    for (LibraryBuilder builder
-        in nextGoodKernelTarget.loader.libraryBuilders) {
-      if (builder is SourceLibraryBuilder) {
-        DillLibraryBuilder dillBuilder =
-            _dillLoadedData!.loader.appendLibrary(builder.library);
-        nextGoodKernelTarget.loader.registerLibraryBuilder(
-            // TODO(johnniwinther): Why do we need to create
-            //  [DillLibraryBuilder]s for the patch library file uris?
-            dillBuilder,
-            builder.isPatch ? builder.fileUri : null);
-        _userBuilders![builder.importUri] = dillBuilder;
-        newDillLibraryBuilders.add(builder.library);
-        changed = true;
-        if (experimentalInvalidation != null) {
-          convertedLibraries ??=
-              new Map<LibraryBuilder, List<LibraryBuilder>>();
-          convertedLibraries[builder] = [dillBuilder];
-        }
+    for (SourceLibraryBuilder builder
+        in nextGoodKernelTarget.loader.sourceLibraryBuilders) {
+      DillLibraryBuilder dillBuilder =
+          _dillLoadedData!.loader.appendLibrary(builder.library);
+      nextGoodKernelTarget.loader.registerLibraryBuilder(dillBuilder);
+      _userBuilders![builder.importUri] = dillBuilder;
+      newDillLibraryBuilders.add(builder.library);
+      changed = true;
+      if (experimentalInvalidation != null) {
+        convertedLibraries ??= new Map<LibraryBuilder, List<LibraryBuilder>>();
+        convertedLibraries[builder] = [dillBuilder];
       }
     }
     if (changed) {
@@ -537,6 +530,8 @@
 
   bool _checkEquivalentScopes(
       SourceLoader sourceLoader, DillLoader dillLoader) {
+    // TODO(johnniwinther): Use [SourceLoader.sourceLibraryBuilders] here.
+    // Currently this causes a failure in incremental_dartino_suite.dart.
     for (LibraryBuilder sourceLibraryBuilder in sourceLoader.libraryBuilders) {
       if (sourceLibraryBuilder is SourceLibraryBuilder) {
         Uri uri = sourceLibraryBuilder.importUri;
@@ -1733,12 +1728,11 @@
       }
 
       SourceLibraryBuilder debugLibrary = new SourceLibraryBuilder(
-        libraryUri,
-        debugExprUri,
-        /*packageUri*/ null,
-        new ImplicitLanguageVersion(libraryBuilder.library.languageVersion),
-        lastGoodKernelTarget.loader,
-        null,
+        importUri: libraryUri,
+        fileUri: debugExprUri,
+        packageLanguageVersion:
+            new ImplicitLanguageVersion(libraryBuilder.library.languageVersion),
+        loader: lastGoodKernelTarget.loader,
         scope: libraryBuilder.scope.createNestedScope("expression"),
         nameOrigin: libraryBuilder,
       );
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 9440adf..031764f0 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -361,23 +361,6 @@
     return entryPoint;
   }
 
-  /// Returns classes defined in libraries in [loader].
-  List<SourceClassBuilder> collectMyClasses() {
-    List<SourceClassBuilder> result = <SourceClassBuilder>[];
-    for (LibraryBuilder library in loader.libraryBuilders) {
-      if (library.loader == loader) {
-        Iterator<Builder> iterator = library.iterator;
-        while (iterator.moveNext()) {
-          Builder member = iterator.current;
-          if (member is SourceClassBuilder && !member.isPatch) {
-            result.add(member);
-          }
-        }
-      }
-    }
-    return result;
-  }
-
   /// The class [cls] is involved in a cyclic definition. This method should
   /// ensure that the cycle is broken, for example, by removing superclass and
   /// implemented interfaces.
@@ -460,10 +443,10 @@
       finishSynthesizedParameters();
       loader.finishDeferredLoadTearoffs();
       loader.finishNoSuchMethodForwarders();
-      List<SourceClassBuilder> myClasses = collectMyClasses();
+      List<SourceClassBuilder> sourceClasses = loader.collectSourceClasses();
       loader.finishNativeMethods();
       loader.finishPatchMethods();
-      finishAllConstructors(myClasses);
+      finishAllConstructors(sourceClasses);
       runBuildTransformations();
 
       if (verify) this.verify();
@@ -625,33 +608,8 @@
 
   void installDefaultSupertypes() {
     Class objectClass = this.objectClass;
-    for (LibraryBuilder library in loader.libraryBuilders) {
-      if (library.loader == loader) {
-        Iterator<Builder> iterator = library.iterator;
-        while (iterator.moveNext()) {
-          Builder declaration = iterator.current;
-          if (declaration is SourceClassBuilder) {
-            Class cls = declaration.cls;
-            if (cls != objectClass) {
-              cls.supertype ??= objectClass.asRawSupertype;
-              declaration.supertypeBuilder ??= new NamedTypeBuilder(
-                  "Object",
-                  const NullabilityBuilder.omitted(),
-                  /* arguments = */ null,
-                  /* fileUri = */ null,
-                  /* charOffset = */ null,
-                  instanceTypeVariableAccess:
-                      InstanceTypeVariableAccessState.Unexpected)
-                ..bind(objectClassBuilder);
-            }
-            if (declaration.isMixinApplication) {
-              cls.mixedInType = declaration.mixedInTypeBuilder!
-                  .buildMixedInType(
-                      library, declaration.charOffset, declaration.fileUri);
-            }
-          }
-        }
-      }
+    for (SourceLibraryBuilder library in loader.sourceLibraryBuilders) {
+      library.installDefaultSupertypes(objectClassBuilder, objectClass);
     }
     ticker.logMs("Installed Object as implicit superclass");
   }
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 8873c4e..23ad100 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -913,7 +913,6 @@
         new ConstructorTearOff(constructorBuilder.member)..parent = literal);
   }
 
-  @override
   int resolveConstructors(SourceLibraryBuilder library) {
     if (constructorReferences == null) return 0;
     for (ConstructorReferenceBuilder ref in constructorReferences!) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 2666f82..10714d1 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -17,6 +17,7 @@
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 
 import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
+import 'package:kernel/core_types.dart';
 
 import 'package:kernel/reference_from_index.dart'
     show IndexedClass, IndexedContainer, IndexedLibrary;
@@ -84,6 +85,7 @@
 import '../kernel/constructor_tearoff_lowering.dart';
 import '../kernel/implicit_field_type.dart';
 import '../kernel/internal_ast.dart';
+import '../kernel/kernel_helper.dart';
 import '../kernel/load_library_builder.dart';
 import '../kernel/type_algorithms.dart'
     show
@@ -122,6 +124,7 @@
 
 import '../type_inference/type_inferrer.dart' show TypeInferrerImpl;
 
+import '../util/helpers.dart';
 import 'name_scheme.dart';
 import 'source_class_builder.dart' show SourceClassBuilder;
 import 'source_extension_builder.dart';
@@ -181,7 +184,7 @@
   @override
   final Library library;
 
-  final SourceLibraryBuilder? actualOrigin;
+  final SourceLibraryBuilder? _origin;
 
   final List<FunctionBuilder> nativeMethods = <FunctionBuilder>[];
 
@@ -256,13 +259,15 @@
   /// [forEachExtensionInScope].
   Set<ExtensionBuilder>? _extensionsInScope;
 
+  List<SourceLibraryBuilder>? _patchLibraries;
+
   SourceLibraryBuilder.internal(
       SourceLoader loader,
       Uri fileUri,
       Uri? packageUri,
       LanguageVersion packageLanguageVersion,
       Scope? scope,
-      SourceLibraryBuilder? actualOrigin,
+      SourceLibraryBuilder? origin,
       Library library,
       LibraryBuilder? nameOrigin,
       Library? referencesFrom,
@@ -274,7 +279,7 @@
             packageLanguageVersion,
             new TypeParameterScopeBuilder.library(),
             scope ?? new Scope.top(),
-            actualOrigin,
+            origin,
             library,
             nameOrigin,
             referencesFrom);
@@ -286,7 +291,7 @@
       this.packageLanguageVersion,
       this._libraryTypeParameterScopeBuilder,
       this.importScope,
-      this.actualOrigin,
+      SourceLibraryBuilder? origin,
       this.library,
       this._nameOrigin,
       this.referencesFrom)
@@ -294,6 +299,7 @@
         currentTypeParameterScopeBuilder = _libraryTypeParameterScopeBuilder,
         referencesFromIndexed =
             referencesFrom == null ? null : new IndexedLibrary(referencesFrom),
+        _origin = origin,
         super(fileUri, _libraryTypeParameterScopeBuilder.toScope(importScope),
             new Scope.top()) {
     assert(
@@ -457,13 +463,13 @@
   }
 
   SourceLibraryBuilder(
-      Uri uri,
-      Uri fileUri,
+      {required Uri importUri,
+      required Uri fileUri,
       Uri? packageUri,
-      LanguageVersion packageLanguageVersion,
-      SourceLoader loader,
-      SourceLibraryBuilder? actualOrigin,
-      {Scope? scope,
+      required LanguageVersion packageLanguageVersion,
+      required SourceLoader loader,
+      SourceLibraryBuilder? origin,
+      Scope? scope,
       Library? target,
       LibraryBuilder? nameOrigin,
       Library? referencesFrom,
@@ -474,10 +480,10 @@
             packageUri,
             packageLanguageVersion,
             scope,
-            actualOrigin,
+            origin,
             target ??
-                (actualOrigin?.library ??
-                    new Library(uri,
+                (origin?.library ??
+                    new Library(importUri,
                         fileUri: fileUri,
                         reference: referenceIsPartOwner == true
                             ? null
@@ -490,6 +496,17 @@
   @override
   bool get isPart => partOfName != null || partOfUri != null;
 
+  // TODO(johnniwinther): Can avoid using this from outside this class?
+  Iterable<SourceLibraryBuilder>? get patchLibraries => _patchLibraries;
+
+  void addPatchLibrary(SourceLibraryBuilder patchLibrary) {
+    assert(patchLibrary.isPatch,
+        "Library ${patchLibrary} must be a patch library.");
+    assert(!patchLibrary.isPart,
+        "Patch library ${patchLibrary} cannot be a part .");
+    (_patchLibraries ??= []).add(patchLibrary);
+  }
+
   List<NamedTypeBuilder> get unresolvedNamedTypes =>
       _libraryTypeParameterScopeBuilder.unresolvedNamedTypes;
 
@@ -1045,6 +1062,19 @@
 
   /// Builds the core AST structure of this library as needed for the outline.
   Library build(LibraryBuilder coreLibrary, {bool modifyTarget: true}) {
+    // TODO(johnniwinther): Avoid the need to process patch libraries before
+    // the origin. Currently, settings performed by the patch are overridden
+    // by the origin. For instance, the `Map` class is abstract in the origin
+    // but (unintentionally) concrete in the patch. By processing the origin
+    // last the `isAbstract` property set by the patch is corrected by the
+    // origin.
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.build(coreLibrary, modifyTarget: modifyTarget);
+      }
+    }
+
     checkMemberConflicts(this, scope,
         checkForInstanceVsStaticConflict: false,
         checkForMethodVsSetterConflict: true);
@@ -1291,9 +1321,9 @@
         (library.problemsAsJson ??= <String>[])
             .addAll(part.library.problemsAsJson!);
       }
-      List<FieldBuilder>? partImplicitlyTypedFields =
-          part.takeImplicitlyTypedFields();
-      if (partImplicitlyTypedFields != null) {
+      List<FieldBuilder> partImplicitlyTypedFields = [];
+      part.collectImplicitlyTypedFields(partImplicitlyTypedFields);
+      if (partImplicitlyTypedFields.isNotEmpty) {
         if (_implicitlyTypedFields == null) {
           _implicitlyTypedFields = partImplicitlyTypedFields;
         } else {
@@ -1324,6 +1354,13 @@
   }
 
   void buildInitialScopes() {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.buildInitialScopes();
+      }
+    }
+
     NameIterator iterator = nameIterator;
     while (iterator.moveNext()) {
       addToExportScope(iterator.name, iterator.current);
@@ -1331,6 +1368,13 @@
   }
 
   void addImportsToScope() {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.addImportsToScope();
+      }
+    }
+
     bool explicitCoreImport = this == loader.coreLibrary;
     for (Import import in imports) {
       if (import.imported?.isPart ?? false) {
@@ -1433,7 +1477,16 @@
   /// Resolves all unresolved types in [unresolvedNamedTypes]. The list of types
   /// is cleared when done.
   int resolveTypes() {
-    int typeCount = unresolvedNamedTypes.length;
+    int typeCount = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        typeCount += patchLibrary.resolveTypes();
+      }
+    }
+
+    typeCount += unresolvedNamedTypes.length;
     for (NamedTypeBuilder namedType in unresolvedNamedTypes) {
       namedType.resolveIn(
           scope, namedType.charOffset!, namedType.fileUri!, this);
@@ -1443,12 +1496,75 @@
     return typeCount;
   }
 
-  @override
-  int resolveConstructors(_) {
-    int count = 0;
+  void installDefaultSupertypes(
+      ClassBuilder objectClassBuilder, Class objectClass) {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.installDefaultSupertypes(objectClassBuilder, objectClass);
+      }
+    }
+
     Iterator<Builder> iterator = this.iterator;
     while (iterator.moveNext()) {
-      count += iterator.current.resolveConstructors(this);
+      Builder declaration = iterator.current;
+      if (declaration is SourceClassBuilder) {
+        Class cls = declaration.cls;
+        if (cls != objectClass) {
+          cls.supertype ??= objectClass.asRawSupertype;
+          declaration.supertypeBuilder ??= new NamedTypeBuilder(
+              "Object",
+              const NullabilityBuilder.omitted(),
+              /* arguments = */ null,
+              /* fileUri = */ null,
+              /* charOffset = */ null,
+              instanceTypeVariableAccess:
+                  InstanceTypeVariableAccessState.Unexpected)
+            ..bind(objectClassBuilder);
+        }
+        if (declaration.isMixinApplication) {
+          cls.mixedInType = declaration.mixedInTypeBuilder!.buildMixedInType(
+              this, declaration.charOffset, declaration.fileUri);
+        }
+      }
+    }
+  }
+
+  void collectSourceClasses(List<SourceClassBuilder> sourceClasses) {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.collectSourceClasses(sourceClasses);
+      }
+    }
+
+    Iterator<Builder> iterator = this.iterator;
+    while (iterator.moveNext()) {
+      Builder member = iterator.current;
+      if (member is SourceClassBuilder && !member.isPatch) {
+        sourceClasses.add(member);
+      }
+    }
+  }
+
+  /// Resolve constructors (lookup names in scope) recorded in this builder and
+  /// return the number of constructors resolved.
+  int resolveConstructors() {
+    int count = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.resolveConstructors();
+      }
+    }
+
+    Iterator<Builder> iterator = this.iterator;
+    while (iterator.moveNext()) {
+      Builder builder = iterator.current;
+      if (builder is SourceClassBuilder) {
+        count += builder.resolveConstructors(this);
+      }
     }
     return count;
   }
@@ -1491,7 +1607,7 @@
   }
 
   @override
-  SourceLibraryBuilder get origin => actualOrigin ?? this;
+  SourceLibraryBuilder get origin => _origin ?? this;
 
   @override
   Uri get importUri => library.importUri;
@@ -2800,10 +2916,45 @@
     return builder;
   }
 
-  @override
-  void buildOutlineExpressions() {
+  void buildOutlineExpressions(
+      CoreTypes coreTypes,
+      List<SynthesizedFunctionNode> synthesizedFunctionNodes,
+      List<DelayedActionPerformer> delayedActionPerformers) {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.buildOutlineExpressions(
+            coreTypes, synthesizedFunctionNodes, delayedActionPerformers);
+      }
+    }
+
     MetadataBuilder.buildAnnotations(
         library, metadata, this, null, null, fileUri, scope);
+
+    Iterator<Builder> iterator = this.iterator;
+    while (iterator.moveNext()) {
+      Builder declaration = iterator.current;
+      if (declaration is ClassBuilder) {
+        declaration.buildOutlineExpressions(
+            this, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
+      } else if (declaration is ExtensionBuilder) {
+        declaration.buildOutlineExpressions(
+            this, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
+      } else if (declaration is MemberBuilder) {
+        declaration.buildOutlineExpressions(
+            this, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
+      } else if (declaration is SourceTypeAliasBuilder) {
+        declaration.buildOutlineExpressions(
+            this, coreTypes, delayedActionPerformers, synthesizedFunctionNodes);
+      } else {
+        assert(
+            declaration is PrefixBuilder ||
+                declaration is DynamicTypeDeclarationBuilder ||
+                declaration is NeverTypeDeclarationBuilder,
+            "Unexpected builder in library: ${declaration} "
+            "(${declaration.runtimeType}");
+      }
+    }
   }
 
   /// Builds the core AST structures for [declaration] needed for the outline.
@@ -3016,9 +3167,16 @@
         suppressMessage: false);
   }
 
-  @override
   int finishDeferredLoadTearoffs() {
     int total = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        total += patchLibrary.finishDeferredLoadTearoffs();
+      }
+    }
+
     for (Import import in imports) {
       if (import.deferred) {
         Procedure? tearoff = import.prefixBuilder!.loadLibraryBuilder!.tearoff;
@@ -3028,12 +3186,20 @@
         total++;
       }
     }
+
     return total;
   }
 
-  @override
   int finishForwarders() {
     int count = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.finishForwarders();
+      }
+    }
+
     CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
     for (int i = 0; i < forwardersOrigins.length; i += 2) {
       Procedure forwarder = forwardersOrigins[i];
@@ -3086,12 +3252,22 @@
     nativeMethods.add(method);
   }
 
-  @override
   int finishNativeMethods() {
+    int count = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.finishNativeMethods();
+      }
+    }
+
     for (FunctionBuilder method in nativeMethods) {
       method.becomeNative(loader);
     }
-    return nativeMethods.length;
+    count += nativeMethods.length;
+
+    return count;
   }
 
   /// Creates a copy of [original] into the scope of [declaration].
@@ -3123,9 +3299,18 @@
     return copy;
   }
 
-  @override
   int finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) {
-    int count = unboundTypeVariables.length;
+    int count = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.finishTypeVariables(object, dynamicType);
+      }
+    }
+
+    count += unboundTypeVariables.length;
+
     // Ensure that type parameters are built after their dependencies by sorting
     // them topologically using references in bounds.
     for (TypeVariableBuilder builder
@@ -3230,9 +3415,20 @@
     _pendingNullabilities.clear();
   }
 
-  @override
+  /// Computes variances of type parameters on typedefs.
+  ///
+  /// The variance property of type parameters on typedefs is computed from the
+  /// use of the parameters in the right-hand side of the typedef definition.
   int computeVariances() {
     int count = 0;
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.computeVariances();
+      }
+    }
+
     for (Builder? declaration
         in _libraryTypeParameterScopeBuilder.members!.values) {
       while (declaration != null) {
@@ -3336,11 +3532,22 @@
     return false;
   }
 
-  @override
+  /// This method instantiates type parameters to their bounds in some cases
+  /// where they were omitted by the programmer and not provided by the type
+  /// inference.  The method returns the number of distinct type variables
+  /// that were instantiated in this library.
   int computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType,
       TypeBuilder bottomType, ClassBuilder objectClass) {
     int count = 0;
 
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.computeDefaultTypes(
+            dynamicType, nullType, bottomType, objectClass);
+      }
+    }
+
     int computeDefaultTypesForVariables(List<TypeVariableBuilder>? variables,
         {required bool inErrorRecovery}) {
       if (variables == null) return 0;
@@ -3560,7 +3767,6 @@
         }
       }
     }
-
     return count;
   }
 
@@ -3628,13 +3834,21 @@
     }
   }
 
-  @override
   int finishPatchMethods() {
-    if (!isPatch) return 0;
     int count = 0;
-    Iterator<Builder> iterator = this.iterator;
-    while (iterator.moveNext()) {
-      count += iterator.current.finishPatch();
+
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        count += patchLibrary.finishPatchMethods();
+      }
+    }
+
+    if (isPatch) {
+      Iterator<Builder> iterator = this.iterator;
+      while (iterator.moveNext()) {
+        count += iterator.current.finishPatch();
+      }
     }
     return count;
   }
@@ -4269,6 +4483,13 @@
   }
 
   void checkTypesInOutline(TypeEnvironment typeEnvironment) {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.checkTypesInOutline(typeEnvironment);
+      }
+    }
+
     Iterator<Builder> iterator = this.iterator;
     while (iterator.moveNext()) {
       Builder declaration = iterator.current;
@@ -4302,6 +4523,13 @@
   }
 
   void computeShowHideElements(ClassMembersBuilder membersBuilder) {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.computeShowHideElements(membersBuilder);
+      }
+    }
+
     assert(currentTypeParameterScopeBuilder.kind ==
         TypeParameterScopeKind.library);
     for (SourceExtensionBuilder extensionBuilder
@@ -4513,11 +4741,19 @@
     (_implicitlyTypedFields ??= <FieldBuilder>[]).add(fieldBuilder);
   }
 
-  @override
-  List<FieldBuilder>? takeImplicitlyTypedFields() {
-    List<FieldBuilder>? result = _implicitlyTypedFields;
-    _implicitlyTypedFields = null;
-    return result;
+  void collectImplicitlyTypedFields(
+      List<FieldBuilder> implicitlyTypedFieldBuilders) {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.collectImplicitlyTypedFields(implicitlyTypedFieldBuilders);
+      }
+    }
+
+    if (_implicitlyTypedFields != null) {
+      implicitlyTypedFieldBuilders.addAll(_implicitlyTypedFields!);
+      _implicitlyTypedFields = null;
+    }
   }
 
   void forEachExtensionInScope(void Function(ExtensionBuilder) f) {
@@ -4583,6 +4819,13 @@
   }
 
   void installTypedefTearOffs() {
+    Iterable<SourceLibraryBuilder>? patches = this.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        patchLibrary.installTypedefTearOffs();
+      }
+    }
+
     Iterator<Builder> iterator = this.iterator;
     while (iterator.moveNext()) {
       Builder? declaration = iterator.current;
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 5568bb4..7bc120f 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -42,7 +42,6 @@
 import '../builder/class_builder.dart';
 import '../builder/constructor_builder.dart';
 import '../builder/declaration_builder.dart';
-import '../builder/dynamic_type_declaration_builder.dart';
 import '../builder/enum_builder.dart';
 import '../builder/extension_builder.dart';
 import '../builder/field_builder.dart';
@@ -51,8 +50,6 @@
 import '../builder/member_builder.dart';
 import '../builder/modifier_builder.dart';
 import '../builder/named_type_builder.dart';
-import '../builder/never_type_declaration_builder.dart';
-import '../builder/prefix_builder.dart';
 import '../builder/procedure_builder.dart';
 import '../builder/type_alias_builder.dart';
 import '../builder/type_builder.dart';
@@ -88,11 +85,10 @@
 import 'source_class_builder.dart' show SourceClassBuilder;
 import 'source_library_builder.dart'
     show
-        LanguageVersion,
-        InvalidLanguageVersion,
         ImplicitLanguageVersion,
+        InvalidLanguageVersion,
+        LanguageVersion,
         SourceLibraryBuilder;
-import 'source_type_alias_builder.dart';
 import 'stack_listener_impl.dart' show offsetForToken;
 
 class SourceLoader extends Loader {
@@ -153,6 +149,8 @@
 
   final Map<Uri, LibraryBuilder> _builders = <Uri, LibraryBuilder>{};
 
+  List<SourceLibraryBuilder>? _sourceLibraryBuilders;
+
   final Queue<LibraryBuilder> _unparsedLibraries = new Queue<LibraryBuilder>();
 
   final List<Library> libraries = <Library>[];
@@ -228,12 +226,28 @@
   @override
   LibraryBuilder? lookupLibraryBuilder(Uri importUri) => _builders[importUri];
 
+  /// The [LibraryBuilder]s for libraries built from source or loaded from dill.
+  ///
+  /// Before [resolveParts] have been called, this includes parts and patches.
   Iterable<LibraryBuilder> get libraryBuilders => _builders.values;
 
+  /// The [SourceLibraryBuilder]s for the libraries built from source by this
+  /// source loader.
+  ///
+  /// This is available after [resolveParts] have been called and doesn't
+  /// include parts or patches. Orphaned parts _are_ included.
+  List<SourceLibraryBuilder> get sourceLibraryBuilders {
+    assert(
+        _sourceLibraryBuilders != null,
+        "Source library builder hasn't been computed yet. "
+        "The source libraries are in SourceLoader.resolveParts.");
+    return _sourceLibraryBuilders!;
+  }
+
   Iterable<Uri> get libraryImportUris => _builders.keys;
 
-  void registerLibraryBuilder(LibraryBuilder libraryBuilder, [Uri? uri]) {
-    uri ??= libraryBuilder.importUri;
+  void registerLibraryBuilder(LibraryBuilder libraryBuilder) {
+    Uri uri = libraryBuilder.importUri;
     if (uri.scheme == "dart" && uri.path == "core") {
       _coreLibrary = libraryBuilder;
     }
@@ -253,8 +267,8 @@
 
   Ticker get ticker => target.ticker;
 
-  /// Creates a [SourceLibraryBuilder] corresponding to [uri], if one doesn't
-  /// exist already.
+  /// Creates a [SourceLibraryBuilder] corresponding to [importUri], if one
+  /// doesn't exist already.
   ///
   /// [fileUri] must not be null and is a URI that can be passed to FileSystem
   /// to locate the corresponding file.
@@ -278,15 +292,20 @@
   /// which the library belongs to, or the current sdk version if the library
   /// doesn't belong to a package.
   SourceLibraryBuilder createLibraryBuilder(
-      Uri uri,
-      Uri fileUri,
+      {required Uri importUri,
+      required Uri fileUri,
       Uri? packageUri,
-      LanguageVersion packageLanguageVersion,
+      required LanguageVersion packageLanguageVersion,
       SourceLibraryBuilder? origin,
       Library? referencesFrom,
-      bool? referenceIsPartOwner) {
+      bool? referenceIsPartOwner}) {
     return new SourceLibraryBuilder(
-        uri, fileUri, packageUri, packageLanguageVersion, this, origin,
+        importUri: importUri,
+        fileUri: fileUri,
+        packageUri: packageUri,
+        packageLanguageVersion: packageLanguageVersion,
+        loader: this,
+        origin: origin,
         referencesFrom: referencesFrom,
         referenceIsPartOwner: referenceIsPartOwner);
   }
@@ -370,13 +389,13 @@
         new ImplicitLanguageVersion(target.currentSdkVersion);
 
     SourceLibraryBuilder libraryBuilder = createLibraryBuilder(
-        uri,
-        fileUri,
-        packageUri,
-        packageLanguageVersion,
-        origin,
-        referencesFrom,
-        referenceIsPartOwner);
+        importUri: uri,
+        fileUri: fileUri,
+        packageUri: packageUri,
+        packageLanguageVersion: packageLanguageVersion,
+        origin: origin,
+        referencesFrom: referencesFrom,
+        referenceIsPartOwner: referenceIsPartOwner);
     if (packageLanguageVersionProblem != null) {
       libraryBuilder.addPostponedProblem(
           packageLanguageVersionProblem, 0, noLength, libraryBuilder.fileUri);
@@ -537,11 +556,9 @@
 
   Future<Null> buildBodies() async {
     assert(_coreLibrary != null);
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        currentUriForCrashReporting = library.importUri;
-        await buildBody(library);
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      currentUriForCrashReporting = library.importUri;
+      await buildBody(library);
     }
     currentUriForCrashReporting = null;
     logSummary(templateSourceBodySummary);
@@ -551,7 +568,12 @@
     ticker.log((Duration elapsed, Duration sinceStart) {
       int libraryCount = 0;
       for (LibraryBuilder library in libraryBuilders) {
-        if (library.loader == this) libraryCount++;
+        if (library.loader == this) {
+          libraryCount++;
+          if (library is SourceLibraryBuilder) {
+            libraryCount += library.patchLibraries?.length ?? 0;
+          }
+        }
       }
       double ms = elapsed.inMicroseconds / Duration.microsecondsPerMillisecond;
       Message message = template.withArguments(
@@ -970,29 +992,34 @@
   }
 
   /// Builds all the method bodies found in the given [library].
-  Future<Null> buildBody(LibraryBuilder library) async {
-    if (library is SourceLibraryBuilder) {
-      // We tokenize source files twice to keep memory usage low. This is the
-      // second time, and the first time was in [buildOutline] above. So this
-      // time we suppress lexical errors.
-      Token tokens = await tokenize(library, suppressLexicalErrors: true);
+  Future<Null> buildBody(SourceLibraryBuilder library) async {
+    Iterable<SourceLibraryBuilder>? patches = library.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        await buildBody(patchLibrary);
+      }
+    }
+
+    // We tokenize source files twice to keep memory usage low. This is the
+    // second time, and the first time was in [buildOutline] above. So this
+    // time we suppress lexical errors.
+    Token tokens = await tokenize(library, suppressLexicalErrors: true);
+    // ignore: unnecessary_null_comparison
+    if (tokens == null) return;
+    DietListener listener = createDietListener(library);
+    DietParser parser = new DietParser(listener);
+    parser.parseUnit(tokens);
+    for (LibraryBuilder part in library.parts) {
+      if (part.partOfLibrary != library) {
+        // Part was included in multiple libraries. Skip it here.
+        continue;
+      }
+      Token tokens = await tokenize(part as SourceLibraryBuilder,
+          suppressLexicalErrors: true);
       // ignore: unnecessary_null_comparison
-      if (tokens == null) return;
-      DietListener listener = createDietListener(library);
-      DietParser parser = new DietParser(listener);
-      parser.parseUnit(tokens);
-      for (LibraryBuilder part in library.parts) {
-        if (part.partOfLibrary != library) {
-          // Part was included in multiple libraries. Skip it here.
-          continue;
-        }
-        Token tokens = await tokenize(part as SourceLibraryBuilder,
-            suppressLexicalErrors: true);
-        // ignore: unnecessary_null_comparison
-        if (tokens != null) {
-          listener.uri = part.fileUri;
-          parser.parseUnit(tokens);
-        }
+      if (tokens != null) {
+        listener.uri = part.fileUri;
+        parser.parseUnit(tokens);
       }
     }
   }
@@ -1068,13 +1095,20 @@
 
   void resolveParts() {
     List<Uri> parts = <Uri>[];
-    List<SourceLibraryBuilder> libraries = <SourceLibraryBuilder>[];
+    List<SourceLibraryBuilder> libraries = [];
+    List<SourceLibraryBuilder> sourceLibraries = [];
+    List<SourceLibraryBuilder> patchLibraries = [];
     _builders.forEach((Uri uri, LibraryBuilder library) {
-      if (library.loader == this) {
+      if (library.loader == this && library is SourceLibraryBuilder) {
         if (library.isPart) {
           parts.add(uri);
         } else {
-          libraries.add(library as SourceLibraryBuilder);
+          if (library.isPatch) {
+            patchLibraries.add(library);
+          } else {
+            sourceLibraries.add(library);
+          }
+          libraries.add(library);
         }
       }
     });
@@ -1093,6 +1127,7 @@
             lookupLibraryBuilder(uri) as SourceLibraryBuilder;
         part.addProblem(messagePartOrphan, 0, 1, part.fileUri);
         part.validatePart(null, null);
+        sourceLibraries.add(part);
       }
     }
     ticker.logMs("Resolved parts");
@@ -1102,6 +1137,25 @@
         library.applyPatches();
       }
     }
+    for (SourceLibraryBuilder patchLibrary in patchLibraries) {
+      _builders.remove(patchLibrary.fileUri);
+      patchLibrary.origin.addPatchLibrary(patchLibrary);
+    }
+    _sourceLibraryBuilders = sourceLibraries;
+    assert(
+        libraryBuilders.every((library) => !library.isPatch),
+        "Patch library found in libraryBuilders: "
+        "${libraryBuilders.where((library) => library.isPatch)}.");
+    assert(
+        sourceLibraries.every((library) => !library.isPatch),
+        "Patch library found in sourceLibraryBuilders: "
+        "${sourceLibraries.where((library) => library.isPatch)}.");
+    assert(
+        libraryBuilders.every((library) =>
+            library.loader != this || sourceLibraries.contains(library)),
+        "Source library not found in sourceLibraryBuilders:"
+        "${libraryBuilders.where((library) => // force line break
+            library.loader == this && !sourceLibraries.contains(library))}");
     ticker.logMs("Applied patches");
   }
 
@@ -1119,6 +1173,19 @@
           exporters.add(exporter.exporter);
         }
       }
+
+      Iterable<SourceLibraryBuilder>? patches =
+          library is SourceLibraryBuilder ? library.patchLibraries : null;
+      if (patches != null) {
+        for (SourceLibraryBuilder patchLibrary in patches) {
+          if (patchLibrary.exporters.isNotEmpty) {
+            exportees.add(patchLibrary);
+            for (Export exporter in patchLibrary.exporters) {
+              exporters.add(exporter.exporter);
+            }
+          }
+        }
+      }
     }
     Set<SourceLibraryBuilder> both = new Set<SourceLibraryBuilder>();
     for (LibraryBuilder exported in exportees) {
@@ -1185,11 +1252,8 @@
 
   void resolveTypes() {
     int typeCount = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        SourceLibraryBuilder sourceLibrary = library as SourceLibraryBuilder;
-        typeCount += sourceLibrary.resolveTypes();
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      typeCount += library.resolveTypes();
     }
     ticker.logMs("Resolved $typeCount types");
   }
@@ -1325,8 +1389,8 @@
       return macros.isNotEmpty ? new MacroApplications(macros) : null;
     }
 
-    for (LibraryBuilder libraryBuilder in libraryBuilders) {
-      if (libraryBuilder.loader != this) continue;
+    for (SourceLibraryBuilder libraryBuilder in sourceLibraryBuilders) {
+      // TODO(johnniwinther): Handle patch libraries.
       LibraryMacroApplicationData libraryMacroApplicationData =
           new LibraryMacroApplicationData();
       Library library = libraryBuilder.library;
@@ -1381,60 +1445,48 @@
 
   void finishDeferredLoadTearoffs() {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.finishDeferredLoadTearoffs();
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.finishDeferredLoadTearoffs();
     }
     ticker.logMs("Finished deferred load tearoffs $count");
   }
 
   void finishNoSuchMethodForwarders() {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.finishForwarders();
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.finishForwarders();
     }
     ticker.logMs("Finished forwarders for $count procedures");
   }
 
   void resolveConstructors() {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.resolveConstructors(null);
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.resolveConstructors();
     }
     ticker.logMs("Resolved $count constructors");
   }
 
   void installTypedefTearOffs() {
     if (target.backendTarget.isTypedefTearOffLoweringEnabled) {
-      for (LibraryBuilder library in libraryBuilders) {
-        if (library.loader == this && library is SourceLibraryBuilder) {
-          library.installTypedefTearOffs();
-        }
+      for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+        library.installTypedefTearOffs();
       }
     }
   }
 
   void finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.finishTypeVariables(object, dynamicType);
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.finishTypeVariables(object, dynamicType);
     }
     ticker.logMs("Resolved $count type-variable bounds");
   }
 
   void computeVariances() {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.computeVariances();
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.computeVariances();
     }
     ticker.logMs("Computed variances of $count type variables");
   }
@@ -1442,31 +1494,25 @@
   void computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType,
       TypeBuilder bottomType, ClassBuilder objectClass) {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.computeDefaultTypes(
-            dynamicType, nullType, bottomType, objectClass);
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.computeDefaultTypes(
+          dynamicType, nullType, bottomType, objectClass);
     }
     ticker.logMs("Computed default types for $count type variables");
   }
 
   void finishNativeMethods() {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.finishNativeMethods();
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.finishNativeMethods();
     }
     ticker.logMs("Finished $count native methods");
   }
 
   void finishPatchMethods() {
     int count = 0;
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        count += library.finishPatchMethods();
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      count += library.finishPatchMethods();
     }
     ticker.logMs("Finished $count patch methods");
   }
@@ -1494,6 +1540,15 @@
     }
   }
 
+  /// Returns classes defined in libraries in this [SourceLoader].
+  List<SourceClassBuilder> collectSourceClasses() {
+    List<SourceClassBuilder> sourceClasses = <SourceClassBuilder>[];
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      library.collectSourceClasses(sourceClasses);
+    }
+    return sourceClasses;
+  }
+
   /// Returns a list of all class builders declared in this loader.  As the
   /// classes are sorted, any cycles in the hierarchy are reported as
   /// errors. Recover by breaking the cycles. This means that the rest of the
@@ -1516,7 +1571,8 @@
     }
 
     // Sort the classes topologically.
-    _SourceClassGraph classGraph = new _SourceClassGraph(this, objectClass);
+    _SourceClassGraph classGraph =
+        new _SourceClassGraph(collectSourceClasses(), objectClass);
     TopologicalSortResult<SourceClassBuilder> result =
         topologicalSort(classGraph);
     List<SourceClassBuilder> classes = result.sortedVertices;
@@ -1661,19 +1717,14 @@
 
   /// Builds the core AST structure needed for the outline of the component.
   void buildComponent() {
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        SourceLibraryBuilder sourceLibrary = library as SourceLibraryBuilder;
-        Library target = sourceLibrary.build(coreLibrary);
-        if (!library.isPatch) {
-          if (sourceLibrary.referencesFrom != null) {
-            referenceFromIndex ??= new ReferenceFromIndex();
-            referenceFromIndex!.addIndexedLibrary(
-                target, sourceLibrary.referencesFromIndexed!);
-          }
-          libraries.add(target);
-        }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      Library target = library.build(coreLibrary);
+      if (library.referencesFrom != null) {
+        referenceFromIndex ??= new ReferenceFromIndex();
+        referenceFromIndex!
+            .addIndexedLibrary(target, library.referencesFromIndexed!);
       }
+      libraries.add(target);
     }
     ticker.logMs("Built component");
   }
@@ -1728,11 +1779,8 @@
   }
 
   void computeShowHideElements() {
-    for (LibraryBuilder libraryBuilder in libraryBuilders) {
-      if (libraryBuilder.loader == this &&
-          libraryBuilder is SourceLibraryBuilder) {
-        libraryBuilder.computeShowHideElements(membersBuilder);
-      }
+    for (SourceLibraryBuilder libraryBuilder in sourceLibraryBuilders) {
+      libraryBuilder.computeShowHideElements(membersBuilder);
     }
     ticker.logMs("Computed show and hide elements");
   }
@@ -1776,13 +1824,8 @@
   }
 
   void checkTypes() {
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library is SourceLibraryBuilder) {
-        if (library.loader == this) {
-          library
-              .checkTypesInOutline(typeInferenceEngine.typeSchemaEnvironment);
-        }
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      library.checkTypesInOutline(typeInferenceEngine.typeSchemaEnvironment);
     }
     ticker.logMs("Checked type arguments of supers against the bounds");
   }
@@ -1857,34 +1900,9 @@
       List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
     List<DelayedActionPerformer> delayedActionPerformers =
         <DelayedActionPerformer>[];
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        (library as SourceLibraryBuilder).buildOutlineExpressions();
-        Iterator<Builder> iterator = library.iterator;
-        while (iterator.moveNext()) {
-          Builder declaration = iterator.current;
-          if (declaration is ClassBuilder) {
-            declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, synthesizedFunctionNodes);
-          } else if (declaration is ExtensionBuilder) {
-            declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, synthesizedFunctionNodes);
-          } else if (declaration is MemberBuilder) {
-            declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, synthesizedFunctionNodes);
-          } else if (declaration is SourceTypeAliasBuilder) {
-            declaration.buildOutlineExpressions(library, coreTypes,
-                delayedActionPerformers, synthesizedFunctionNodes);
-          } else {
-            assert(
-                declaration is PrefixBuilder ||
-                    declaration is DynamicTypeDeclarationBuilder ||
-                    declaration is NeverTypeDeclarationBuilder,
-                "Unexpected builder in library: ${declaration} "
-                "(${declaration.runtimeType}");
-          }
-        }
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      library.buildOutlineExpressions(
+          coreTypes, synthesizedFunctionNodes, delayedActionPerformers);
     }
     for (DelayedActionPerformer delayedActionPerformer
         in delayedActionPerformers) {
@@ -1918,14 +1936,8 @@
     membersBuilder.computeTypes();
 
     List<FieldBuilder> allImplicitlyTypedFields = <FieldBuilder>[];
-    for (LibraryBuilder library in libraryBuilders) {
-      if (library.loader == this) {
-        List<FieldBuilder>? implicitlyTypedFields =
-            library.takeImplicitlyTypedFields();
-        if (implicitlyTypedFields != null) {
-          allImplicitlyTypedFields.addAll(implicitlyTypedFields);
-        }
-      }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      library.collectImplicitlyTypedFields(allImplicitlyTypedFields);
     }
 
     for (int i = 0; i < allImplicitlyTypedFields.length; i++) {
@@ -2011,27 +2023,48 @@
         isTopLevel: isTopLevel);
   }
 
-  void checkMainMethods() {
-    DartType? listOfString;
+  void _checkMainMethods(
+      SourceLibraryBuilder libraryBuilder, DartType listOfString) {
+    Iterable<SourceLibraryBuilder>? patches = libraryBuilder.patchLibraries;
+    if (patches != null) {
+      for (SourceLibraryBuilder patchLibrary in patches) {
+        _checkMainMethods(patchLibrary, listOfString);
+      }
+    }
 
-    for (LibraryBuilder libraryBuilder in libraryBuilders) {
-      if (libraryBuilder.loader == this &&
-          libraryBuilder.isNonNullableByDefault) {
-        Builder? mainBuilder =
-            libraryBuilder.exportScope.lookupLocalMember('main', setter: false);
-        mainBuilder ??=
-            libraryBuilder.exportScope.lookupLocalMember('main', setter: true);
-        if (mainBuilder is MemberBuilder) {
-          if (mainBuilder is InvalidTypeDeclarationBuilder) {
-            // This is an ambiguous export, skip the check.
-            return;
+    if (libraryBuilder.isNonNullableByDefault) {
+      Builder? mainBuilder =
+          libraryBuilder.exportScope.lookupLocalMember('main', setter: false);
+      mainBuilder ??=
+          libraryBuilder.exportScope.lookupLocalMember('main', setter: true);
+      if (mainBuilder is MemberBuilder) {
+        if (mainBuilder is InvalidTypeDeclarationBuilder) {
+          // This is an ambiguous export, skip the check.
+          return;
+        }
+        if (mainBuilder.isField ||
+            mainBuilder.isGetter ||
+            mainBuilder.isSetter) {
+          if (mainBuilder.parent != libraryBuilder) {
+            libraryBuilder.addProblem(messageMainNotFunctionDeclarationExported,
+                libraryBuilder.charOffset, noLength, libraryBuilder.fileUri,
+                context: [
+                  messageExportedMain.withLocation(mainBuilder.fileUri!,
+                      mainBuilder.charOffset, mainBuilder.name.length)
+                ]);
+          } else {
+            libraryBuilder.addProblem(
+                messageMainNotFunctionDeclaration,
+                mainBuilder.charOffset,
+                mainBuilder.name.length,
+                mainBuilder.fileUri);
           }
-          if (mainBuilder.isField ||
-              mainBuilder.isGetter ||
-              mainBuilder.isSetter) {
+        } else {
+          Procedure procedure = mainBuilder.member as Procedure;
+          if (procedure.function.requiredParameterCount > 2) {
             if (mainBuilder.parent != libraryBuilder) {
               libraryBuilder.addProblem(
-                  messageMainNotFunctionDeclarationExported,
+                  messageMainTooManyRequiredParametersExported,
                   libraryBuilder.charOffset,
                   noLength,
                   libraryBuilder.fileUri,
@@ -2041,17 +2074,42 @@
                   ]);
             } else {
               libraryBuilder.addProblem(
-                  messageMainNotFunctionDeclaration,
+                  messageMainTooManyRequiredParameters,
                   mainBuilder.charOffset,
                   mainBuilder.name.length,
                   mainBuilder.fileUri);
             }
-          } else {
-            Procedure procedure = mainBuilder.member as Procedure;
-            if (procedure.function.requiredParameterCount > 2) {
+          } else if (procedure.function.namedParameters
+              .any((parameter) => parameter.isRequired)) {
+            if (mainBuilder.parent != libraryBuilder) {
+              libraryBuilder.addProblem(
+                  messageMainRequiredNamedParametersExported,
+                  libraryBuilder.charOffset,
+                  noLength,
+                  libraryBuilder.fileUri,
+                  context: [
+                    messageExportedMain.withLocation(mainBuilder.fileUri!,
+                        mainBuilder.charOffset, mainBuilder.name.length)
+                  ]);
+            } else {
+              libraryBuilder.addProblem(
+                  messageMainRequiredNamedParameters,
+                  mainBuilder.charOffset,
+                  mainBuilder.name.length,
+                  mainBuilder.fileUri);
+            }
+          } else if (procedure.function.positionalParameters.length > 0) {
+            DartType parameterType =
+                procedure.function.positionalParameters.first.type;
+
+            if (!typeEnvironment.isSubtypeOf(listOfString, parameterType,
+                SubtypeCheckMode.withNullabilities)) {
               if (mainBuilder.parent != libraryBuilder) {
                 libraryBuilder.addProblem(
-                    messageMainTooManyRequiredParametersExported,
+                    templateMainWrongParameterTypeExported.withArguments(
+                        parameterType,
+                        listOfString,
+                        libraryBuilder.isNonNullableByDefault),
                     libraryBuilder.charOffset,
                     noLength,
                     libraryBuilder.fileUri,
@@ -2061,84 +2119,40 @@
                     ]);
               } else {
                 libraryBuilder.addProblem(
-                    messageMainTooManyRequiredParameters,
+                    templateMainWrongParameterType.withArguments(parameterType,
+                        listOfString, libraryBuilder.isNonNullableByDefault),
                     mainBuilder.charOffset,
                     mainBuilder.name.length,
                     mainBuilder.fileUri);
               }
-            } else if (procedure.function.namedParameters
-                .any((parameter) => parameter.isRequired)) {
-              if (mainBuilder.parent != libraryBuilder) {
-                libraryBuilder.addProblem(
-                    messageMainRequiredNamedParametersExported,
-                    libraryBuilder.charOffset,
-                    noLength,
-                    libraryBuilder.fileUri,
-                    context: [
-                      messageExportedMain.withLocation(mainBuilder.fileUri!,
-                          mainBuilder.charOffset, mainBuilder.name.length)
-                    ]);
-              } else {
-                libraryBuilder.addProblem(
-                    messageMainRequiredNamedParameters,
-                    mainBuilder.charOffset,
-                    mainBuilder.name.length,
-                    mainBuilder.fileUri);
-              }
-            } else if (procedure.function.positionalParameters.length > 0) {
-              DartType parameterType =
-                  procedure.function.positionalParameters.first.type;
-
-              listOfString ??= new InterfaceType(
-                  coreTypes.listClass,
-                  Nullability.nonNullable,
-                  [coreTypes.stringNonNullableRawType]);
-
-              if (!typeEnvironment.isSubtypeOf(listOfString, parameterType,
-                  SubtypeCheckMode.withNullabilities)) {
-                if (mainBuilder.parent != libraryBuilder) {
-                  libraryBuilder.addProblem(
-                      templateMainWrongParameterTypeExported.withArguments(
-                          parameterType,
-                          listOfString,
-                          libraryBuilder.isNonNullableByDefault),
-                      libraryBuilder.charOffset,
-                      noLength,
-                      libraryBuilder.fileUri,
-                      context: [
-                        messageExportedMain.withLocation(mainBuilder.fileUri!,
-                            mainBuilder.charOffset, mainBuilder.name.length)
-                      ]);
-                } else {
-                  libraryBuilder.addProblem(
-                      templateMainWrongParameterType.withArguments(
-                          parameterType,
-                          listOfString,
-                          libraryBuilder.isNonNullableByDefault),
-                      mainBuilder.charOffset,
-                      mainBuilder.name.length,
-                      mainBuilder.fileUri);
-                }
-              }
             }
           }
-        } else if (mainBuilder != null) {
-          if (mainBuilder.parent != libraryBuilder) {
-            libraryBuilder.addProblem(messageMainNotFunctionDeclarationExported,
-                libraryBuilder.charOffset, noLength, libraryBuilder.fileUri,
-                context: [
-                  messageExportedMain.withLocation(
-                      mainBuilder.fileUri!, mainBuilder.charOffset, noLength)
-                ]);
-          } else {
-            libraryBuilder.addProblem(messageMainNotFunctionDeclaration,
-                mainBuilder.charOffset, noLength, mainBuilder.fileUri);
-          }
+        }
+      } else if (mainBuilder != null) {
+        if (mainBuilder.parent != libraryBuilder) {
+          libraryBuilder.addProblem(messageMainNotFunctionDeclarationExported,
+              libraryBuilder.charOffset, noLength, libraryBuilder.fileUri,
+              context: [
+                messageExportedMain.withLocation(
+                    mainBuilder.fileUri!, mainBuilder.charOffset, noLength)
+              ]);
+        } else {
+          libraryBuilder.addProblem(messageMainNotFunctionDeclaration,
+              mainBuilder.charOffset, noLength, mainBuilder.fileUri);
         }
       }
     }
   }
 
+  void checkMainMethods() {
+    DartType listOfString = new InterfaceType(coreTypes.listClass,
+        Nullability.nonNullable, [coreTypes.stringNonNullableRawType]);
+
+    for (SourceLibraryBuilder libraryBuilder in sourceLibraryBuilders) {
+      _checkMainMethods(libraryBuilder, listOfString);
+    }
+  }
+
   void releaseAncillaryResources() {
     hierarchy = null;
     _hierarchyBuilder = null;
@@ -2453,26 +2467,13 @@
 
 class _SourceClassGraph implements Graph<SourceClassBuilder> {
   @override
-  final List<SourceClassBuilder> vertices = [];
+  final List<SourceClassBuilder> vertices;
   final ClassBuilder _objectClass;
   final Map<SourceClassBuilder, Map<TypeDeclarationBuilder?, TypeAliasBuilder?>>
       directSupertypeMap = {};
   final Map<SourceClassBuilder, List<SourceClassBuilder>> _supertypeMap = {};
 
-  _SourceClassGraph(SourceLoader loader, this._objectClass) {
-    // Compute the vertices as all classes declared in this loader.
-    for (LibraryBuilder library in loader.libraryBuilders) {
-      if (library.loader == loader) {
-        Iterator<Builder> members = library.iterator;
-        while (members.moveNext()) {
-          Builder member = members.current;
-          if (member is SourceClassBuilder && !member.isPatch) {
-            vertices.add(member);
-          }
-        }
-      }
-    }
-  }
+  _SourceClassGraph(this.vertices, this._objectClass);
 
   List<SourceClassBuilder> computeSuperClasses(SourceClassBuilder cls) {
     Map<TypeDeclarationBuilder?, TypeAliasBuilder?> directSupertypes =
diff --git a/pkg/front_end/test/fasta/generator_to_string_test.dart b/pkg/front_end/test/fasta/generator_to_string_test.dart
index 5fdfef0..f4790b9 100644
--- a/pkg/front_end/test/fasta/generator_to_string_test.dart
+++ b/pkg/front_end/test/fasta/generator_to_string_test.dart
@@ -84,18 +84,17 @@
     Expression index = new VariableGet(new VariableDeclaration("index"));
     UriTranslator uriTranslator = await c.options.getUriTranslator();
     SourceLibraryBuilder libraryBuilder = new SourceLibraryBuilder(
-        uri,
-        uri,
-        /*packageUri*/ null,
-        new ImplicitLanguageVersion(defaultLanguageVersion),
-        new KernelTarget(
+        importUri: uri,
+        fileUri: uri,
+        packageLanguageVersion:
+            new ImplicitLanguageVersion(defaultLanguageVersion),
+        loader: new KernelTarget(
                 const MockFileSystem(),
                 false,
                 new DillTarget(c.options.ticker, uriTranslator,
                     new NoneTarget(new TargetFlags())),
                 uriTranslator)
-            .loader,
-        null);
+            .loader);
     libraryBuilder.markLanguageVersionFinal();
     LoadLibraryBuilder loadLibraryBuilder =
         new LoadLibraryBuilder(libraryBuilder, dummyLibraryDependency, -1);
diff --git a/pkg/front_end/test/patching/data/abstract_class/libraries.json b/pkg/front_end/test/patching/data/abstract_class/libraries.json
new file mode 100644
index 0000000..a697508
--- /dev/null
+++ b/pkg/front_end/test/patching/data/abstract_class/libraries.json
@@ -0,0 +1,12 @@
+{
+  "none": {
+    "libraries": {
+      "test": {
+        "patches": [
+          "patch.dart"
+        ],
+        "uri": "origin.dart"
+      }
+    }
+  }
+}
diff --git a/pkg/front_end/test/patching/data/abstract_class/main.dart b/pkg/front_end/test/patching/data/abstract_class/main.dart
new file mode 100644
index 0000000..faf7854
--- /dev/null
+++ b/pkg/front_end/test/patching/data/abstract_class/main.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, 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.
+
+/*cfe.library: nnbd=false*/
+
+/*cfe:nnbd.library: nnbd=true*/
+
+// ignore: uri_does_not_exist
+import 'dart:test';
+
+void test(Class cls) {}
+
+main() {}
diff --git a/pkg/front_end/test/patching/data/abstract_class/origin.dart b/pkg/front_end/test/patching/data/abstract_class/origin.dart
new file mode 100644
index 0000000..c8359e2
--- /dev/null
+++ b/pkg/front_end/test/patching/data/abstract_class/origin.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2021, 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.
+
+/*cfe.library: nnbd=false*/
+
+/*cfe:nnbd.library: nnbd=true*/
+
+/*class: Interface:
+ isAbstract,
+ kernel-members=[
+  Interface.,
+  interfaceMethod],
+ scope=[interfaceMethod]
+*/
+/*member: Interface.:initializers=[SuperInitializer]*/
+abstract class Interface {
+  void interfaceMethod();
+}
+
+/*class: Class:
+ isAbstract,
+ kernel-members=[
+  Class.,
+  classMethod],
+ scope=[classMethod]
+*/
+/*member: Class.:initializers=[SuperInitializer]*/
+abstract class Class implements Interface {
+  external void classMethod();
+}
diff --git a/pkg/front_end/test/patching/data/abstract_class/patch.dart b/pkg/front_end/test/patching/data/abstract_class/patch.dart
new file mode 100644
index 0000000..9d11f24
--- /dev/null
+++ b/pkg/front_end/test/patching/data/abstract_class/patch.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, 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.
+
+// ignore: import_internal_library
+import 'dart:_internal';
+
+@patch
+class Class {
+  @patch
+  /*member: Class.classMethod:patch*/
+  void classMethod() {}
+}
diff --git a/pkg/front_end/test/patching/patching_test.dart b/pkg/front_end/test/patching/patching_test.dart
index 1cd2220..60129e5 100644
--- a/pkg/front_end/test/patching/patching_test.dart
+++ b/pkg/front_end/test/patching/patching_test.dart
@@ -118,6 +118,7 @@
   static const String error = 'message';
   static const String isNonNullableByDefault = 'nnbd';
   static const String patch = 'patch';
+  static const String isAbstract = 'isAbstract';
 }
 
 class PatchingDataExtractor extends CfeDataExtractor<Features> {
@@ -137,6 +138,9 @@
     ClassBuilder clsBuilder = lookupClassBuilder(compilerResult, cls)!;
 
     Features features = new Features();
+    if (cls.isAbstract) {
+      features.add(Tags.isAbstract);
+    }
     clsBuilder.scope.forEach((String name, Builder builder) {
       features.addElement(Tags.scope, name);
     });
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 89cc303..9b57834 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -292,6 +292,7 @@
 dartbug
 dartdoc
 dartfix
+dartino
 dartlang
 dashes
 dc
@@ -1243,6 +1244,7 @@
 substitutes
 substitutor
 suggests
+suite
 sum
 summarizing
 superclasses
@@ -1383,6 +1385,7 @@
 unify
 uninstantiable
 uninstantiated
+unintentionally
 unions
 uniqueness
 unittest
diff --git a/pkg/front_end/tool/dart_doctest_impl.dart b/pkg/front_end/tool/dart_doctest_impl.dart
index 64d0aa7..9be9af8 100644
--- a/pkg/front_end/tool/dart_doctest_impl.dart
+++ b/pkg/front_end/tool/dart_doctest_impl.dart
@@ -826,12 +826,11 @@
   SourceLibraryBuilder createDartDocTestLibrary(
       SourceLoader loader, LibraryBuilder libraryBuilder) {
     SourceLibraryBuilder dartDocTestLibrary = new SourceLibraryBuilder(
-      dartDocTestUri,
-      dartDocTestUri,
-      /*packageUri*/ null,
-      new ImplicitLanguageVersion(libraryBuilder.library.languageVersion),
-      loader,
-      null,
+      importUri: dartDocTestUri,
+      fileUri: dartDocTestUri,
+      packageLanguageVersion:
+          new ImplicitLanguageVersion(libraryBuilder.library.languageVersion),
+      loader: loader,
       scope: libraryBuilder.scope.createNestedScope("dartdoctest"),
       nameOrigin: libraryBuilder,
     );
@@ -899,14 +898,14 @@
 
   @override
   SourceLibraryBuilder createLibraryBuilder(
-      Uri uri,
-      Uri fileUri,
+      {required Uri importUri,
+      required Uri fileUri,
       Uri? packageUri,
-      LanguageVersion packageLanguageVersion,
+      required LanguageVersion packageLanguageVersion,
       SourceLibraryBuilder? origin,
       kernel.Library? referencesFrom,
-      bool? referenceIsPartOwner) {
-    if (uri == DocTestIncrementalCompiler.dartDocTestUri) {
+      bool? referenceIsPartOwner}) {
+    if (importUri == DocTestIncrementalCompiler.dartDocTestUri) {
       HybridFileSystem hfs = target.fileSystem as HybridFileSystem;
       MemoryFileSystem fs = hfs.memory;
       fs
@@ -915,7 +914,13 @@
       return compiler.createDartDocTestLibrary(
           this, compiler._dartDocTestLibraryBuilder!);
     }
-    return super.createLibraryBuilder(uri, fileUri, packageUri,
-        packageLanguageVersion, origin, referencesFrom, referenceIsPartOwner);
+    return super.createLibraryBuilder(
+        importUri: importUri,
+        fileUri: fileUri,
+        packageUri: packageUri,
+        packageLanguageVersion: packageLanguageVersion,
+        origin: origin,
+        referencesFrom: referencesFrom,
+        referenceIsPartOwner: referenceIsPartOwner);
   }
 }