@@ -662,7 +662,7 @@ ways.
662
662
663
663
** TODO** : Explain library cycles and compiling to library augmentations.
664
664
665
- ### Macro compilation order
665
+ ### Library cycles
666
666
667
667
Applying a macro involves executing the Dart code inside the body of the macro.
668
668
Obviously, that code must be type-checked and compiled before it can be run. To
@@ -687,35 +687,137 @@ have the following restrictions:
687
687
688
688
[ modules ] : https://github.com/dart-lang/language/tree/master/working/modules
689
689
690
- ### Complete macro application order
690
+ A Dart implementation can enforce this restriction by organizing a program into
691
+ ** library cycles** . The build package [ already does this] [ build lib cycle ] . A
692
+ library cycle is a set of libraries containing import cycles. If two libraries
693
+ are not in the same cycle, it is guaranteed that there is no cyclic import
694
+ between them.
691
695
692
- When all of these are put together, an idealized compilation and macro
693
- application of a Dart program looks like this:
696
+ ### Ideal compilation process
694
697
695
- ** TODO** : Update to use library cycles.
698
+ Here's a (non-normative) illustration of how a Dart implementation could compile
699
+ a Dart program containing macro applications:
696
700
697
- 1 . For each library, ordered topologically by imports:
701
+ #### 1. Break the program into library cycles
698
702
699
- 1 . For each declaration, with nested declarations ordered first:
703
+ Starting at the entrypoint library, traverse all imports, exports, and
704
+ augmentation imports to collect the full graph of libraries to be compiled.
705
+ Calculate the [ strongly connected components] [ ] of this graph. Each component is
706
+ a library cycle, and the edges between them determine how the cycles depend on
707
+ each other. Sort the library cycles in topological order based on the connected
708
+ component graph.
700
709
701
- 1 . Apply each phase 1 macro to the declaration, from right to left.
710
+ Report an error if macro application and its definition occur in the same
711
+ library cycle.
702
712
703
- 1 . At this point, all top level identifiers can be resolved.
713
+ Each library cycle can now be fully compiled separately. When compiling a
714
+ library cycle, it is guaranteed that all macros used by the cycle have already
715
+ been compiled. Also, any types or other declarations used by that cycle have
716
+ either already been compiled, or are defined in that cycle.
704
717
705
- 1 . For each declaration, with nested declarations ordered first:
718
+ [ strongly connected components ] : https://en.wikipedia.org/wiki/Strongly_connected_component
706
719
707
- 1 . Apply each phase 2 macro to the declaration, from right to left.
720
+ #### 2. Compile each cycle
721
+
722
+ Go through the library cycles in topological order. For each cycle, compile all
723
+ of its libraries. First, merge in any hand-authored library augmentations into
724
+ their libraries. At this point, you have a set of mutually interdependent
725
+ libraries. They may contain references to declarations that don't exist because
726
+ macros have yet to produce them.
727
+
728
+ Collect all the metadata annotations whose names can be resolved and that
729
+ resolve to macro classes. Report an error if any application refers to a macro
730
+ declared in this cycle.
731
+
732
+ ** TODO** : The above resolution rules may change based on
733
+ https://github.com/dart-lang/language/issues/1890 .
734
+
735
+ #### 3. Apply macros
736
+
737
+ In a sandbox environment or isolate, create an instance of the corresponding
738
+ macro class for each macro application. Pass in any macro application arguments
739
+ to the macro's constructor. If a parameter's type is ` Code ` or a subclass,
740
+ convert the argument expression to a ` Code ` object. Any bare identifiers in the
741
+ argument expression are converted to ` Identifier ` instances whose scope is the
742
+ library of the macro application.
743
+
744
+ Run all of the macros in phase order:
745
+
746
+ 1 . Invoke the corresponding visit method for all macros that implement phase 1
747
+ APIs.
748
+
749
+ 1 . Invoke the corresponding visit method for all macros that implement phase 2
750
+ APIs.
751
+
752
+ 1 . Invoke the corresponding visit method for all macros that implement phase 3
753
+ APIs.
754
+
755
+ While these are running, the macro will likely call back into the host
756
+ environment to introspect over code in the current library cycle or previously
757
+ compiled cycles. The introspection API is mostly syntactic and structural: a
758
+ macro can walk the members on a class declaration or look at the * name* of a
759
+ type annotation without the compiler having to do any resolution or type
760
+ checking.
761
+
762
+ When a macro wants to resolve an identifier in a type annotation, there is an
763
+ explicit API for that. When that happens, the implementation attempts to resolve
764
+ the identifier and return a reference to the resolved declaration. Macros do not
765
+ have introspection access to the imperative code of a library, so that code
766
+ doesn't need to be resolved or type-checked at this point.
767
+
768
+ Meanwhile, the macro is also producing new declarations and definitions. These
769
+ are collected and held by the macro processor. When introspecting over code, the
770
+ implementation needs to show not just the state of the code on disk, but any of
771
+ these new declarations produced previously by macros.
772
+
773
+ #### 4. Generate an augmentation library
774
+
775
+ Once all macro applications have finished running, the implementation creates a
776
+ new empty augmentation library for each library containing macro applications.
777
+ All of the declarations created by macros and held by the processor are now
778
+ added to the augmentation.
779
+
780
+ Entirely new declarations are simply added to the augmentation library as
781
+ declarations. Declarations that wrap the original declaration's code are added
782
+ as augmenting declarations. If a macro adds members to a type, then the type is
783
+ added to the augmentation library as an augmenting type, and the members are
784
+ added into that.
785
+
786
+ ** TODO** : How are name collisions from private declarations handled?
787
+
788
+ The ` Code ` objects representing the signature and body of the declaration is
789
+ serialized to Dart source. ` Code ` objects created from strings are inserted
790
+ verbatim into the augmentation library. It's up to the macro author to take
791
+ care when using unqualified identifiers in string-based ` Code ` objects.
792
+
793
+ ** TODO** : Do we want to find identifiers in string-based ` Code ` objects and
794
+ implicitly scope them somehow?
708
795
709
- 1 . At this point, all declarations and their signatures exist. The library
710
- can be type checked.
711
-
712
- 1 . For each declaration, with nested declarations ordered first:
713
-
714
- 1 . Apply each phase 3 macro to the declaration, from right to left.
715
-
716
- 1 . Now all macros have been applied, all imperative code exists, and the
717
- library can be completely compiled. Any macros defined in this library
718
- are ready to be used by later libraries.
796
+ Instances of the ` Identifier ` class have special serialization. An import for
797
+ library that the identifier resolves to is added to the augmentation with a new
798
+ unique prefix identifier. The ` Identifier ` name is then serialized as a prefixed
799
+ identifier for that prefix. (A more sophisticated implementation could reuse
800
+ imports when multiple identifiers resolve to the same library, and may choose to
801
+ omit the prefix entirely if the resulting identifier will still resolve
802
+ correctly.)
803
+
804
+ This augmentation library may be written on disk in some implementation-defined
805
+ location. It should be accessible to users so that it's possible to step into
806
+ and debug macro-generated code. It should probably * not* be stored directly next
807
+ to their source code. We don't expect users to commit these generated files to
808
+ source control.
809
+
810
+ #### 5. Apply augmentation and compile
811
+
812
+ Finally, the implementation implicitly applies these macro-generated
813
+ augmentation libraries onto their corresponding main libraries. After that, all
814
+ of the libraries are fully complete. They should contain no unresolvable
815
+ identifiers, even in imperative code, and every declared member should have a
816
+ definition. Report an error if that's not true.
817
+
818
+ Otherwise, all of the libraries in the cycle can be fully compiled and the
819
+ implementation can move on to the next cycle. Any macros declared in this cycle
820
+ are ready to be loaded and executed when applied in libraries in later cycles.
719
821
720
822
## Executing macros
721
823
0 commit comments