pamidur / aspect-injector

AOP framework for .NET (c#, vb, etc)
Apache License 2.0
745 stars 112 forks source link

"Specified method is not supported" error after upgrading to 2.4.3 #142

Closed hectorjsmith closed 3 years ago

hectorjsmith commented 3 years ago

First off, thanks for the great work. This library is really useful 👍


I recently updated the AspectInjector library on one of my projects from version 2.2.5 to 2.4.3. I made no changes to any of the aspects or the injections. But as soon as I try to build the project with the new version, I get the following error: AspectInjector|2.4.3 : error AI_ERR0: Processing failure: System.NotSupportedException: Specified method is not supported

Since I have such a large number of injections in this project, I'm not able to easily track down which method(s) are causing the problem. Do you have any advice on how to find the source of the error?

Or since the project continues to work using an older version, is this a bug/regression? I have reverted the update and the build is working again.

For reference, the full stacktrace:

3>AspectInjector|2.4.3: Found 1 aspects, 1956 injections
3>AspectInjector|2.4.3 : error AI_ERR0: Processing failure: System.NotSupportedException: Specified method is not supported.
3>   at Mono.Cecil.SignatureWriter.WriteTypeSignature(TypeReference type)
3>   at Mono.Cecil.SignatureWriter.WriteGenericInstanceSignature(IGenericInstance instance)
3>   at Mono.Cecil.MetadataBuilder.GetTypeSpecToken(TypeReference type)
3>   at Mono.Cecil.MetadataBuilder.AddConstraints(GenericParameter generic_parameter, GenericParamConstraintTable table)
3>   at Mono.Cecil.MetadataBuilder.AddGenericParameters()
3>   at Mono.Cecil.MetadataBuilder.BuildModule()
3>   at Mono.Cecil.MetadataBuilder.BuildMetadata()
3>   at Mono.Cecil.ModuleWriter.<>c.<BuildMetadata>b__2_0(MetadataBuilder builder, MetadataReader _)
3>   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read)
3>   at Mono.Cecil.ModuleWriter.BuildMetadata(ModuleDefinition module, MetadataBuilder metadata)
3>   at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
3>   at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
3>   at Mono.Cecil.ModuleDefinition.Write(Stream stream, WriterParameters parameters)
3>   at FluentIL.PatcherBase.WriteAssembly(AssemblyDefinition assembly, Boolean writeSymbols, Boolean verbose)
3>   at FluentIL.PatcherBase.Process(String assemblyFile, IAssemblyResolver resolver, Boolean optimize, Boolean verbose)
3>   at AspectInjector.Compiler.Execute(String filename, IReadOnlyList`1 references, Boolean optimize, Boolean verbose). Please submit an issue to https://github.com/pamidur/aspect-injector
3>AspectInjector : error AI_FAIL: Aspect Injector processing has failed. See other errors.

Thanks

pamidur commented 3 years ago

Thank you for feedback and report. I'll take a look and get back to you shortly

pamidur commented 3 years ago

I've investigated this issue a little. It looks some kind of regression with generic constraints. I've also tried to reproduce it without success tho. You could help by finding and sharing your most weird/complex generic constraints.

hectorjsmith commented 3 years ago

Thanks for looking into it so quick.

I had a quick look around and I think the strangest/most complex generic constraints we use are recursive generics. We use them in places to allow interface methods to return their own type - e.g. for method chaining.

I simplified it down a lot so that this example hopefully makes some sense.

interface IMyResult<TResultType>
{
    TResultType RawResult { get; }
}

interface IMyInterface<TResult, TResultType, TRecursive>
    where TResult : IMyResult<TResultType>
    where TRecursive : IMyInterface<TResult, TResultType, TRecursive>
{
    TResult Result { get; }
    TRecursive Me { get; }
}

class MyClass : IMyInterface<IMyResult<string>, string, MyClass>
{
    public IMyResult<string> Result => throw new NotImplementedException();
    public MyClass Me => this;
}
pamidur commented 3 years ago

Thank you for sharing. I actually was able to reproduce this(or similar) issue. I'll be looking into how to fix, however I'm afraid I have no ETA for a fix, you might want to stay on 2.2.5 until then :(

hectorjsmith commented 3 years ago

No problem, thank you for working on a fix. I'll keep an eye on this issue for any updates :+1:

pamidur commented 3 years ago

@hectorjsmith , could you please test v2.4.4-pre4 ? It should fix the issue

hectorjsmith commented 3 years ago

I tested out v2.4.4-pre4 but I still have the same issue :/ The good news is that I was able to narrow it down to a single function.

I can reproduce the issue with the interfaces/classes below:

interface IOne { }

interface ITwo<T>
    where T : IOne { }

interface IThree<T, U>
    where T : IOne
    where U : ITwo<T> { }

[MyAspect]
class Class1
{
    public V MyFunction<T, U, V>(
            Func<V> generatorFunc1,
            Func<int, IEnumerable<T>> generatorFunc2,
            Func<string, T, U> generatorFunc3)
        where T : IOne
        where U : ITwo<T>
        where V : IThree<T, U>
    {
        throw new NotImplementedException();
    }
}
pamidur commented 3 years ago

Could you please also share your MyAspect definition?

hectorjsmith commented 3 years ago

You can find the exact aspect definition were are using here.

The pieces I think you will be most interested in are below for reference.

[Aspect(Scope.Global)]
[Injection(typeof(PerformanceLoggingAttribute))]
public class PerformanceLoggingAttribute : System.Attribute
{
    [Advice(Kind.Around)]
    public object HandleAround(
        [Argument(Source.Name)] string methodName,
        [Argument(Source.Arguments)] object[] arguments,
        [Argument(Source.Target)] Func<object[], object> method)
    {
        // ...
    }

    [Advice(Kind.Before)]
    public void HandleBefore(
        [Argument(Source.Name)] string methodName,
        [Argument(Source.Instance)] object instance)
    {
        // ...
    }

    [Advice(Kind.After)]
    public void HandleAfter(
        [Argument(Source.Name)] string methodName)
    {
        // ...
    }
}
pamidur commented 3 years ago

Thanks, I narrowed it down to 'Around' Advice. I can reproduce it now!

pamidur commented 3 years ago

@hectorjsmith , could you please test v2.4.4-pre5 ?

hectorjsmith commented 3 years ago

v2.4.4-pre5 works perfectly! Thank you for the quick resolution :+1:

pamidur commented 3 years ago

Thanks for helping make this project better!