Aspect weavers
Normal aspects are implemented by the BuildAspect method, which provides advice thanks to the advice factory exposed by the IAspectBuilder interface. Therefore, normal aspects are limited to the abilities of the IAdviceFactory interface.
By contrast, aspect weavers allow you to perform completely arbitrary transformations on C# code using the low-level Roslyn API.
When you assign an aspect weaver to an aspect class, Metalama no longer calls the BuildAspect method to implement the aspect, but instead calls the aspect weaver.
Unlike normal aspects, weaver-based aspects:
- are one or two orders of magnitude more complex to implement;
- are not executed at design time;
- require their own implementation project;
- may have a significant impact on compilation performance, especially when there are many of them.
Creating a weaver-based aspect
The following steps guide you to the process of creating a weaver-based aspect and its weaver:
Step 1. Create the solution scaffolding
This step is described in Creating a Metalama SDK solution structure.
Step 2. Define the public interface of your aspect (typically a custom attribute)
In the public API project created in the previous step, define an aspect class as usual, e.g.
public class AutoCancellationAttribute : TypeAspect { }
Step 3. Create the weaver for this aspect
In the weaver project created in Step 1:
- Add a class that implements the IAspectWeaver interface.
- Make sure the class is
public
. - Add the MetalamaPlugInAttribute custom attributes to this class.
At this point, the code will look like this:
using Metalama.Compiler;
using Metalama.Framework.Engine.AspectWeavers;
namespace Metalama.Open.AutoCancellationToken;
[MetalamaPlugIn]
internal partial class AutoCancellationTokenWeaver : IAspectWeaver
{
public Task TransformAsync( AspectWeaverContext context )
{
throw new NotImplementedException();
}
}
Step 4. Bind the aspect class to its weaver class
Go back to the aspect class and annotate it with a custom attribute of type RequireAspectWeaverAttribute. The constructor argument must be the namespace-qualified name of the weaver class.
[RequireAspectWeaver("Metalama.Open.AutoCancellationToken.AutoCancellationTokenWeaver")]
public class AutoCancellationAttribute : TypeAspect { }
Step 5. Implement the TransformAsync method
TransformAsync has a parameter of type AspectWeaverContext. The Compilation property of this object contains the input compilation, and your implementation must set this property to the new compilation.
The type of the Compilation property is IPartialCompilation. Compilations are immutable objects. This interface, as well as the extension class PartialCompilationExtensions, offer different methods to transform the compilation. For instance, the RewriteSyntaxTreesAsync method will apply a rewriter to the input compilation and return the resulting compilation.
Do not forget to write back the context.Compilation property.
Each weaver will be invoked a single time per project, regardless of the number of aspect instances in the project.
The list of aspect instances that need to be handled by your weaver is given by the context.AspectInstances property.
To map the Metalama code model to an ISymbol
, use the extension methods in SymbolExtensions.
Your weaver does not need to format the output code itself. This is done by Metalama at the end of the pipeline, and only when necessary.
However, your weaver is responsible for annotating the syntax nodes with the annotations declared in the FormattingAnnotations class.
Step 6. Write unit tests
If you have created a test project as described in Creating a Metalama SDK solution structure, you can test your weaver-based aspect as any other aspect.
Examples
Available examples of Metalama.Framework.Sdk weavers are:
- Metalama.Open.Virtuosity: makes all possible methods in a project
virtual
. - Metalama.Open.AutoCancellationToken: automatically propagates
CancellationToken
parameter. - Metalama.Open.DependencyEmbedder: bundles .NET Framework applications into a single executable file.
The Metalama.Open.Virtuosity repository contains very little logic, so it can be used as a template for your own weavers.