[cfe] Compute computation layers from precompiled libraries

The previous computation of compilation layers used source/dill to
determine which macro libraries need to be precompiled. Since the
target of the macro runtime (the VM) and the output might not be the
same, we need to use the provided precompiled libraries to determine
which macros that need to be precompiled.

Change-Id: Id39e0cff5f85b4f37a16ac531d97c53f8971ecce
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229140
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder_graph.dart b/pkg/front_end/lib/src/fasta/builder_graph.dart
index 9e2b434..fdae514 100644
--- a/pkg/front_end/lib/src/fasta/builder_graph.dart
+++ b/pkg/front_end/lib/src/fasta/builder_graph.dart
@@ -23,13 +23,15 @@
 class BuilderGraph implements Graph<Uri> {
   final Map<Uri, LibraryBuilder> builders;
 
+  final Map<Uri, List<Uri>> _neighborsCache = {};
+
   BuilderGraph(this.builders);
 
   @override
   Iterable<Uri> get vertices => builders.keys;
 
-  @override
-  Iterable<Uri> neighborsOf(Uri vertex) sync* {
+  List<Uri> _computeNeighborsOf(Uri vertex) {
+    List<Uri> neighbors = [];
     LibraryBuilder? library = builders[vertex];
     if (library == null) {
       throw "Library not found: $vertex";
@@ -40,20 +42,20 @@
         if (import.imported != null) {
           Uri uri = import.imported!.importUri;
           if (builders.containsKey(uri)) {
-            yield uri;
+            neighbors.add(uri);
           }
         }
       }
       for (Export export in library.exports) {
         Uri uri = export.exported.importUri;
         if (builders.containsKey(uri)) {
-          yield uri;
+          neighbors.add(uri);
         }
       }
       for (LibraryBuilder part in library.parts) {
         Uri uri = part.importUri;
         if (builders.containsKey(uri)) {
-          yield uri;
+          neighbors.add(uri);
         }
       }
     } else if (library is DillLibraryBuilder) {
@@ -61,7 +63,7 @@
       for (LibraryDependency dependency in library.library.dependencies) {
         Uri uri = dependency.targetLibrary.importUri;
         if (builders.containsKey(uri)) {
-          yield uri;
+          neighbors.add(uri);
         }
       }
 
@@ -69,9 +71,14 @@
       for (LibraryPart part in library.library.parts) {
         Uri uri = getPartUri(library.importUri, part);
         if (builders.containsKey(uri)) {
-          yield uri;
+          neighbors.add(uri);
         }
       }
     }
+    return neighbors;
   }
+
+  @override
+  Iterable<Uri> neighborsOf(Uri vertex) =>
+      _neighborsCache[vertex] ??= _computeNeighborsOf(vertex);
 }
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 e8b0af8..f481250 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -1364,26 +1364,42 @@
       dataForTesting!.macroDeclarationData.macrosAreAvailable = true;
     }
 
-    Set<ClassBuilder> macroClasses = {macroClassBuilder};
-    Set<Uri> macroLibraries = {macroLibraryBuilder.importUri};
+    Set<ClassBuilder> macroClasses = {};
 
-    for (SourceLibraryBuilder sourceLibraryBuilder in sourceLibraryBuilders) {
-      Iterator<Builder> iterator = sourceLibraryBuilder.iterator;
+    /// Libraries containing macros that need compilation.
+    Set<Uri> macroLibraries = {};
+
+    /// Libraries containing precompiled macro classes.
+    Set<Uri> precompiledMacroLibraries = {};
+
+    Map<MacroClass, Uri> precompiledMacroUris =
+        target.context.options.precompiledMacroUris;
+
+    for (LibraryBuilder libraryBuilder in libraryBuilders) {
+      Iterator<Builder> iterator = libraryBuilder.iterator;
       while (iterator.moveNext()) {
         Builder builder = iterator.current;
-        if (builder is SourceClassBuilder && builder.isMacro) {
-          macroClasses.add(builder);
-          macroLibraries.add(builder.library.importUri);
-          if (retainDataForTesting) {
-            (dataForTesting!.macroDeclarationData
-                    .macroDeclarations[builder.library.importUri] ??= [])
-                .add(builder.name);
+        if (builder is ClassBuilder && builder.isMacro) {
+          Uri libraryUri = builder.library.importUri;
+          MacroClass macroClass = new MacroClass(libraryUri, builder.name);
+          if (!precompiledMacroUris.containsKey(macroClass)) {
+            macroClasses.add(builder);
+            macroLibraries.add(libraryUri);
+            if (retainDataForTesting) {
+              (dataForTesting!.macroDeclarationData
+                      .macroDeclarations[libraryUri] ??= [])
+                  .add(builder.name);
+            }
+          } else {
+            precompiledMacroLibraries.add(libraryUri);
           }
         }
       }
     }
 
-    bool isDillLibrary(Uri uri) => _builders[uri]?.loader != this;
+    if (macroClasses.isEmpty) {
+      return;
+    }
 
     List<List<Uri>> computeCompilationSequence(Graph<Uri> libraryGraph,
         {required bool Function(Uri) filter}) {
@@ -1418,9 +1434,39 @@
       return layeredComponents;
     }
 
-    List<List<Uri>> compilationSteps = computeCompilationSequence(
-        new BuilderGraph(_builders),
-        filter: isDillLibrary);
+    Graph<Uri> graph = new BuilderGraph(_builders);
+
+    /// Libraries that are considered precompiled. These are libraries that are
+    /// either given as precompiled macro libraries, or libraries that these
+    /// depend upon.
+    // TODO(johnniwinther): Can we assume that the precompiled dills are
+    // self-contained?
+    Set<Uri> precompiledLibraries = {};
+
+    void addPrecompiledLibrary(Uri uri) {
+      if (precompiledLibraries.add(uri)) {
+        for (Uri neighbor in graph.neighborsOf(uri)) {
+          addPrecompiledLibrary(neighbor);
+        }
+      }
+    }
+
+    for (LibraryBuilder builder in _builders.values) {
+      if (builder.loader != this) {
+        addPrecompiledLibrary(builder.importUri);
+      } else if (precompiledMacroLibraries.contains(builder.importUri)) {
+        assert(
+            !macroLibraries.contains(builder.importUri),
+            "Macro library ${builder.importUri} is only partially "
+            "precompiled.");
+        addPrecompiledLibrary(builder.importUri);
+      }
+    }
+
+    bool isPrecompiledLibrary(Uri uri) => precompiledLibraries.contains(uri);
+
+    List<List<Uri>> compilationSteps =
+        computeCompilationSequence(graph, filter: isPrecompiledLibrary);
     if (retainDataForTesting) {
       dataForTesting!.macroDeclarationData.compilationSequence =
           compilationSteps;
diff --git a/pkg/front_end/test/macros/data/package_config.json b/pkg/front_end/test/macros/data/package_config.json
index 9de8189..f176d49 100644
--- a/pkg/front_end/test/macros/data/package_config.json
+++ b/pkg/front_end/test/macros/data/package_config.json
@@ -6,6 +6,10 @@
       "rootUri": "pkgs/macro/lib/"
     },
     {
+      "name": "precompiled_macro",
+      "rootUri": "pkgs/precompiled_macro/lib/"
+    },
+    {
       "name": "_fe_analyzer_shared",
       "rootUri": "../../../../_fe_analyzer_shared/lib/"
     }
diff --git a/pkg/front_end/test/macros/data/pkgs/precompiled_macro/lib/precompiled_macro.dart b/pkg/front_end/test/macros/data/pkgs/precompiled_macro/lib/precompiled_macro.dart
new file mode 100644
index 0000000..8d112ca
--- /dev/null
+++ b/pkg/front_end/test/macros/data/pkgs/precompiled_macro/lib/precompiled_macro.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+import 'src/macro_base.dart';
+
+macro class PrecompiledMacro extends MacroBase implements Macro {
+  const PrecompiledMacro();
+}
diff --git a/pkg/front_end/test/macros/data/pkgs/precompiled_macro/lib/src/macro_base.dart b/pkg/front_end/test/macros/data/pkgs/precompiled_macro/lib/src/macro_base.dart
new file mode 100644
index 0000000..e503d1b
--- /dev/null
+++ b/pkg/front_end/test/macros/data/pkgs/precompiled_macro/lib/src/macro_base.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2022, 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.
+
+class MacroBase {
+  const MacroBase();
+}
diff --git a/pkg/front_end/test/macros/data/tests/all_precompiled.dart b/pkg/front_end/test/macros/data/tests/all_precompiled.dart
new file mode 100644
index 0000000..9310930
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/all_precompiled.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2022, 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.
+
+/*library: 
+ macroClassIds=[package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro],
+ macroInstanceIds=[package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro/()],
+ macrosAreApplied,
+ macrosAreAvailable
+*/
+
+import 'package:precompiled_macro/precompiled_macro.dart';
+
+/*member: main:appliedMacros=[PrecompiledMacro.new]*/
+@PrecompiledMacro()
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/applications.dart b/pkg/front_end/test/macros/data/tests/applications.dart
index 4201d8c..06bf716 100644
--- a/pkg/front_end/test/macros/data/tests/applications.dart
+++ b/pkg/front_end/test/macros/data/tests/applications.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  package:macro/macro.dart,
+  package:_fe_analyzer_shared/src/macros/api.dart|package:macro/macro.dart,
   main.dart],
  macroClassIds=[
   package:macro/macro.dart/Macro1,
diff --git a/pkg/front_end/test/macros/data/tests/declare_macro.dart b/pkg/front_end/test/macros/data/tests/declare_macro.dart
index 056edbd..65611e1 100644
--- a/pkg/front_end/test/macros/data/tests/declare_macro.dart
+++ b/pkg/front_end/test/macros/data/tests/declare_macro.dart
@@ -3,9 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /*library: 
- compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  main.dart],
+ compilationSequence=[main.dart|package:_fe_analyzer_shared/src/macros/api.dart],
  declaredMacros=[MyMacro],
  macrosAreAvailable
 */
diff --git a/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart b/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart
index 1a6c048..69ea910 100644
--- a/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart
+++ b/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  apply_lib_dep.dart|macro_lib_dep.dart|main_lib_dep.dart|package:_fe_analyzer_shared/src/macros/api.dart,
-  macro_lib.dart,
+  apply_lib_dep.dart|macro_lib.dart|macro_lib_dep.dart|main_lib_dep.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   apply_lib.dart|main.dart],
  macroClassIds=[macro_lib.dart/Macro1],
  macroInstanceIds=[macro_lib.dart/Macro1/()],
diff --git a/pkg/front_end/test/macros/data/tests/direct_import.dart b/pkg/front_end/test/macros/data/tests/direct_import.dart
index a07f128..793a5f4 100644
--- a/pkg/front_end/test/macros/data/tests/direct_import.dart
+++ b/pkg/front_end/test/macros/data/tests/direct_import.dart
@@ -2,12 +2,7 @@
 // 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.
 
-/*library: 
- compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  main.dart],
- macrosAreAvailable
-*/
+/*library: macrosAreAvailable*/
 
 // ignore: unused_import
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_builder.dart b/pkg/front_end/test/macros/data/tests/import_macro_builder.dart
index a07f128..793a5f4 100644
--- a/pkg/front_end/test/macros/data/tests/import_macro_builder.dart
+++ b/pkg/front_end/test/macros/data/tests/import_macro_builder.dart
@@ -2,12 +2,7 @@
 // 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.
 
-/*library: 
- compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  main.dart],
- macrosAreAvailable
-*/
+/*library: macrosAreAvailable*/
 
 // ignore: unused_import
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_package.dart b/pkg/front_end/test/macros/data/tests/import_macro_package.dart
index 0c25eea..9619bb1 100644
--- a/pkg/front_end/test/macros/data/tests/import_macro_package.dart
+++ b/pkg/front_end/test/macros/data/tests/import_macro_package.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  package:macro/macro.dart,
+  package:_fe_analyzer_shared/src/macros/api.dart|package:macro/macro.dart,
   main.dart],
  macrosAreAvailable
 */
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart b/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
index dce865b..15c660c 100644
--- a/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
+++ b/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  macro_lib.dart,
+  macro_lib.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   main.dart],
  macrosAreAvailable
 */
diff --git a/pkg/front_end/test/macros/data/tests/macro_declarations.dart b/pkg/front_end/test/macros/data/tests/macro_declarations.dart
index b6cb47f..c780d61 100644
--- a/pkg/front_end/test/macros/data/tests/macro_declarations.dart
+++ b/pkg/front_end/test/macros/data/tests/macro_declarations.dart
@@ -3,9 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /*library: 
- compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  main.dart],
+ compilationSequence=[main.dart|package:_fe_analyzer_shared/src/macros/api.dart],
  declaredMacros=[
   Extends,
   ExtendsAlias,
diff --git a/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart b/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart
index 4aeb2a7..873d202 100644
--- a/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart
+++ b/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  macro_lib1.dart|macro_lib2a.dart,
+  macro_lib1.dart|macro_lib2a.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   macro_lib2b.dart,
   main.dart],
  macroClassIds=[
diff --git a/pkg/front_end/test/macros/data/tests/precompiled.dart b/pkg/front_end/test/macros/data/tests/precompiled.dart
new file mode 100644
index 0000000..5b29de2
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/precompiled.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, 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.
+
+/*library: 
+ compilationSequence=[
+  package:macro/macro.dart,
+  main.dart],
+ macroClassIds=[
+  package:macro/macro.dart/Macro1,
+  package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro],
+ macroInstanceIds=[
+  package:macro/macro.dart/Macro1/(),
+  package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro/()],
+ macrosAreApplied,
+ macrosAreAvailable
+*/
+
+import 'package:precompiled_macro/precompiled_macro.dart';
+import 'package:macro/macro.dart';
+
+/*member: main:appliedMacros=[
+  Macro1.new,
+  PrecompiledMacro.new]*/
+@PrecompiledMacro()
+@Macro1()
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_package.dart b/pkg/front_end/test/macros/data/tests/use_macro_package.dart
index 691f58a..c3b123e 100644
--- a/pkg/front_end/test/macros/data/tests/use_macro_package.dart
+++ b/pkg/front_end/test/macros/data/tests/use_macro_package.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  package:macro/macro.dart,
+  package:_fe_analyzer_shared/src/macros/api.dart|package:macro/macro.dart,
   main.dart],
  macroClassIds=[
   package:macro/macro.dart/Macro1,
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart b/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
index 5bc5073..eff50a1 100644
--- a/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
+++ b/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
@@ -4,8 +4,7 @@
 
 /*library: 
  compilationSequence=[
-  package:_fe_analyzer_shared/src/macros/api.dart,
-  macro_lib.dart,
+  macro_lib.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   main.dart],
  macroClassIds=[
   macro_lib.dart/Macro1,
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
index 756e587..25ee5db 100644
--- a/pkg/front_end/test/macros/macro_test.dart
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -43,6 +43,11 @@
       CompilerOptions options, TestData testData) {
     TestMacroExecutor macroExecutor = new TestMacroExecutor();
     options.macroExecutorProvider = () async => macroExecutor;
+    Uri precompiledPackage =
+        Uri.parse('package:precompiled_macro/precompiled_macro.dart');
+    options.precompiledMacroUris = {
+      new MacroClass(precompiledPackage, 'PrecompiledMacro'): dummyUri,
+    };
     return macroExecutor;
   }
 }
@@ -295,10 +300,6 @@
   @override
   Future<MacroClassIdentifier> loadMacro(Uri library, String name,
       {Uri? precompiledKernelUri}) async {
-    if (precompiledKernelUri != null) {
-      throw new UnsupportedError(
-          'Precompiled kernel not supported for this implementation.');
-    }
     _MacroClassIdentifier id = new _MacroClassIdentifier(library, name);
     macroClasses.add(id);
     return id;