| Severity | Error |
| Reported by | Metalama.Framework |
| Message | The aspect '<AspectName>' makes an extension contribution, such as an interceptor registration, but it executes after a low-level aspect weaver, and contributions are processed only before the first low-level aspect weaver. Order the aspect before the aspects that require a low-level weaver. |
Cause
Aspects normally transform code with the high-level advising API. Some aspects need a low-level weaver, written with the Metalama SDK, and bind to it with [RequireAspectWeaver]. A low-level weaver runs after the high-level aspects that precede it and rewrites the compilation directly.
Some aspects also make extension contributions: they register data that a Metalama extension, such as Metalama.Extensions.Interceptors, processes once for the whole pipeline. For instance, an aspect registers interceptors with the InterceptMethods method. Extensions process only the contributions made before the first low-level aspect weaver, because after this weaver the compilation has been rewritten.
Metalama reports LAMA0091 when an aspect makes an extension contribution but is ordered to execute after an aspect that requires a low-level weaver. In the message, <AspectName> is the aspect that made the contribution. The error is reported on the target of that aspect.
In the following example, InterceptWrite registers an interceptor, but the aspect order makes it execute after Audit, which requires a low-level weaver. InterceptPrint, which executes before Audit, is processed normally. A second AspectOrder attribute orders Audit after the contract aspects of the referenced libraries, so that the weaver doesn't fall between the layers of one of them (see LAMA0042).
1using Metalama.Extensions.Interceptors;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using Metalama.Framework.Engine;
5using Metalama.Framework.Engine.AspectWeavers;
6using System;
7using System.Linq;
8using System.Threading.Tasks;
9
10// The [InterceptWrite] aspect executes after the weaver of the [Audit] aspect.
11[assembly: AspectOrder(
12 AspectOrderDirection.RunTime,
13 typeof(Doc.LAMA0091.Error.InterceptWriteAttribute),
14 typeof(Doc.LAMA0091.Error.AuditAttribute),
15 typeof(Doc.LAMA0091.Error.InterceptPrintAttribute) )]
16
17// The weaver of [Audit] must not fall between the layers of the contract aspects of the referenced libraries
18// (LAMA0042), so it is ordered after all of them.
19[assembly: AspectOrder( AspectOrderDirection.RunTime, typeof(Doc.LAMA0091.Error.AuditAttribute), typeof(ContractAspect) )]
20
21namespace Doc.LAMA0091.Error;
22
23// A low-level aspect, which requires a low-level aspect weaver.
24[RequireAspectWeaver( "Doc.LAMA0091.Error.AuditWeaver" )]
25internal class AuditAttribute : TypeAspect { }
26
27[MetalamaPlugIn]
28internal class AuditWeaver : IAspectWeaver
29{
30 public Task TransformAsync( AspectWeaverContext context ) => Task.CompletedTask;
31}
32
33// This aspect registers an interceptor for the Print method.
34internal class InterceptPrintAttribute : TypeAspect
35{
36 public override void BuildAspect( IAspectBuilder<INamedType> builder )
37 => builder.InterceptMethods(
38 b => b
39 .Type( typeof(Target) ).Methods( "Print" )
40 .RedirectToExistingMethod( typeof(Redirections).AsINamedType().Methods.OfName( "Print" ).Single() ) );
41}
42
43// This aspect registers an interceptor for the Write method.
44internal class InterceptWriteAttribute : TypeAspect
45{
46 public override void BuildAspect( IAspectBuilder<INamedType> builder )
47 => builder.InterceptMethods(
48 b => b
49 .Type( typeof(Target) ).Methods( "Write" )
50 .RedirectToExistingMethod( typeof(Redirections).AsINamedType().Methods.OfName( "Write" ).Single() ) );
51}
52
53internal static class Target
54{
55 public static void Print( string value ) => Console.WriteLine( "target: " + value );
56
57 public static void Write( string value ) => Console.WriteLine( "target: " + value );
58}
59
60internal static class Redirections
61{
62 public static void Print( string value ) => Console.WriteLine( "intercepted: " + value );
63
64 public static void Write( string value ) => Console.WriteLine( "intercepted: " + value );
65}
66
1namespace Doc.LAMA0091.Error;
2
3// Error: [InterceptWrite] executes after the weaver of [Audit], so its interceptor cannot be registered.
4[InterceptWrite]
5[Audit]
6[InterceptPrint]
Error LAMA0091: The aspect 'InterceptWrite' makes an extension contribution, such as an interceptor registration, but it executes after a low-level aspect weaver, and contributions are processed only before the first low-level aspect weaver. Order the aspect before the aspects that require a low-level weaver.
7internal static class Program
8{
9 public static void Run()
10 {
11 Target.Print( "a" );
12 Target.Write( "b" );
13 }
14}
15
Resolution
Order the aspect that makes the extension contribution before every aspect that requires a low-level weaver, with the [AspectOrder] attribute. In the following example, Audit executes after both interceptor aspects.
1using Metalama.Extensions.Interceptors;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using Metalama.Framework.Engine;
5using Metalama.Framework.Engine.AspectWeavers;
6using System;
7using System.Linq;
8using System.Threading.Tasks;
9
10// Both interceptor aspects execute before the weaver of the [Audit] aspect.
11[assembly: AspectOrder(
12 AspectOrderDirection.RunTime,
13 typeof(Doc.LAMA0091.Fixed.AuditAttribute),
14 typeof(Doc.LAMA0091.Fixed.InterceptWriteAttribute),
15 typeof(Doc.LAMA0091.Fixed.InterceptPrintAttribute) )]
16
17// The weaver of [Audit] must not fall between the layers of the contract aspects of the referenced libraries
18// (LAMA0042), so it is ordered after all of them.
19[assembly: AspectOrder( AspectOrderDirection.RunTime, typeof(Doc.LAMA0091.Fixed.AuditAttribute), typeof(ContractAspect) )]
20
21namespace Doc.LAMA0091.Fixed;
22
23// A low-level aspect, which requires a low-level aspect weaver.
24[RequireAspectWeaver( "Doc.LAMA0091.Fixed.AuditWeaver" )]
25internal class AuditAttribute : TypeAspect { }
26
27[MetalamaPlugIn]
28internal class AuditWeaver : IAspectWeaver
29{
30 public Task TransformAsync( AspectWeaverContext context ) => Task.CompletedTask;
31}
32
33// This aspect registers an interceptor for the Print method.
34internal class InterceptPrintAttribute : TypeAspect
35{
36 public override void BuildAspect( IAspectBuilder<INamedType> builder )
37 => builder.InterceptMethods(
38 b => b
39 .Type( typeof(Target) ).Methods( "Print" )
40 .RedirectToExistingMethod( typeof(Redirections).AsINamedType().Methods.OfName( "Print" ).Single() ) );
41}
42
43// This aspect registers an interceptor for the Write method.
44internal class InterceptWriteAttribute : TypeAspect
45{
46 public override void BuildAspect( IAspectBuilder<INamedType> builder )
47 => builder.InterceptMethods(
48 b => b
49 .Type( typeof(Target) ).Methods( "Write" )
50 .RedirectToExistingMethod( typeof(Redirections).AsINamedType().Methods.OfName( "Write" ).Single() ) );
51}
52
53internal static class Target
54{
55 public static void Print( string value ) => Console.WriteLine( "target: " + value );
56
57 public static void Write( string value ) => Console.WriteLine( "target: " + value );
58}
59
60internal static class Redirections
61{
62 public static void Print( string value ) => Console.WriteLine( "intercepted: " + value );
63
64 public static void Write( string value ) => Console.WriteLine( "intercepted: " + value );
65}
66
1namespace Doc.LAMA0091.Fixed;
2
3// Fixed: both interceptor aspects are ordered before [Audit].
4[InterceptWrite]
5[Audit]
6[InterceptPrint]
7internal static class Program
8{
9 public static void Run()
10 {
11 Target.Print( "a" );
12 Target.Write( "b" );
13 }
14}
15
1namespace Doc.LAMA0091.Fixed;
2
3// Fixed: both interceptor aspects are ordered before [Audit].
4[InterceptWrite]
5[Audit]
6[InterceptPrint]
7internal static class Program
8{
9 public static void Run()
10 {
11 Redirections.Print("a");
12 Redirections.Write("b");
13 }
14}
15