The foreword 0.

Above: THERE are several implementations of AOP

Next talk about how to do the AOP demo, first use Csharp to say dynamic weaving and static weaving, there is time to talk about Java corresponding content.

Roslyn will do a JIT AOP demo.

Why do you say that?

In fact, because Roslyn already includes all parts of JIT, I don’t need to mention any JIT implementation. (good)

So here’s what this piece actually says:

How to introduce Roslyn to JIT compiled code

What does proxy-pattern AOP look like

Why not recommend playing like this in a production environment without optimization?

1. JIT compiled code

Roslyn is. NET compiler, interested can see the documentation docs.microsoft.com/en-us/dotne…

In fact, Roslyn has already made it very simple, with a few lines of code coming in to compile cSharp code.

If you don’t believe me, we’ll write one

1.1 Introducing the Roslyn package

< ItemGroup > < PackageReference Include = "Microsoft. CodeAnalysis. CSharp" Version = "3.6.0" / > < PackageReference . Include = "System. The Runtime Loader" Version = "4.3.0" / > < / ItemGroup >Copy the code

1.2 Code to syntax tree

public SyntaxTree ParseToSyntaxTree(string code) { var parseOptions = new CSharpParseOptions(LanguageVersion.Latest, preprocessorSymbols: new[] { "RELEASE" }); / / there are many other configuration items, the simplest of these is ok return CSharpSyntaxTree. ParseText (code, parseOptions); }Copy the code

1.3 Preparing the compiler instance

public CSharpCompilation BuildCompilation(SyntaxTree syntaxTree) { var compilationOptions = new CSharpCompilationOptions( concurrentBuild: true, metadataImportOptions: MetadataImportOptions.All, outputKind: OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release, allowUnsafe: true, platform: Platform.AnyCpu, checkOverflow: false, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default); / / there are many other configuration items, the simplest of these is ok var references = AppDomain. CurrentDomain. GetAssemblies () Where (I = >! i.IsDynamic && ! string.IsNullOrWhiteSpace(i.Location)) .Distinct() .Select(i => MetadataReference.CreateFromFile(i.Location)); // Get the DLL used for compilation. Return csharpcompilation. Create("code.cs", new SyntaxTree[] {SyntaxTree}, References, compilationOptions); }Copy the code

1.4 Compilation to memory

public Assembly ComplieToAssembly(CSharpCompilation compilation)
{
    using (var stream = new MemoryStream())
    {
        var restult = compilation.Emit(stream);
        if (restult.Success)
        {
            stream.Seek(0, SeekOrigin.Begin);
            return AssemblyLoadContext.Default.LoadFromStream(stream);
        }
        else
        {
            throw new Exception(restult.Diagnostics.Select(i => i.ToString()).DefaultIfEmpty().Aggregate((i, j) => i + j));
        }
    }
}
Copy the code

1.5 Test it out

static void TestJIT()
{
     var code = @"
    public class HiJ
    {
        public void Test(string v)
        {
            System.Console.WriteLine($""Hi, {v}!"");
        }
    }";
    var jit = new Jit();
    var syntaxTree = jit.ParseToSyntaxTree(code);
    var compilation = jit.BuildCompilation(syntaxTree);
    var assembly = jit.ComplieToAssembly(compilation);
    // test
    foreach (var item in assembly.GetTypes())
    {
        Console.WriteLine(item.FullName);
        item.GetMethod("Test").Invoke(Activator.CreateInstance(item), new object[] { "joker" });
    }
}
Copy the code

Running results:

HiJ

Hi, joker!
Copy the code



That’s it. You can just JIT whatever you want.

Implement AOP in a proxy manner

2.1 Review what is agency

Just to clarify what an agent is,

After all, many AOP frameworks and others have the idea of leveraging proxies,

Why do we have to play like this?



Quite simply, proxies do the same thing for you, more than you do, without touching your original code at all.

For example, the real class and the proxy class look exactly the same

But the real code for both might look something like this

RealClass: public class RealClass { public virtual int Add(int i, int j) { return i + j; } }  ProxyClass: public class ProxyClass : RealClass { public override int Add(int i, int j) { int r = 0; i += 7; j -= 7; r = base.Add(i, j); r += 55; return r; }}Copy the code

So it’s going to look like this when we call it

2.2 Make a Proxy code generator

So let’s create a ProxyClass code generator that generates exactly the same code as the example above

First of all, we all know that cSharp can be used to retrieve metadata by reflection.

We know the metadata, so we can spell out the strings to spell out the code we want (yes, you read that right, we spell out the strings)

Show you code

public class ProxyGenerator { public string GenerateProxyCode(Type type, Action<StringBuilder, MethodBase> beforeCall, Action<StringBuilder, MethodBase> afterCall) { var sb = new StringBuilder(); sb.Append($"{(type.IsPublic ? "public" : "")} class {type.Name}Proxy : {type.Name} {{ "); foreach (var method in type.GetTypeInfo().DeclaredMethods) { GenerateProxyMethod(beforeCall, afterCall, sb, method); } sb.Append(" }"); return sb.ToString(); } private static void GenerateProxyMethod(Action<StringBuilder, MethodBase> beforeCall, Action<StringBuilder, MethodBase> afterCall, StringBuilder sb, MethodInfo method) { var ps = method.GetParameters().Select(p => $"{p.ParameterType.FullName} {p.Name}"); sb.Append($"{(method.IsPublic ? "public" : "")} override {method.ReturnType.FullName} {method.Name}({string.Join(",", ps)}) {{"); sb.Append($"{method.ReturnType.FullName} r = default;" ); beforeCall(sb, method); sb.Append($"r = base.{method.Name}({string.Join(",", method.GetParameters().Select(p => p.Name))});" ); afterCall(sb, method); sb.Append("return r; } "); }}Copy the code

Test the

public static class TestProxyGenerator
{
    public static void Test()
    {
        var generator = new ProxyGenerator();
        var code = generator.GenerateProxyCode(typeof(RealClass), (sb, method) => { }, (sb, method) => { sb.Append("r++;"); });
        Console.WriteLine(code);
    }
}
Copy the code

Results:

public class RealClassProxy : RealClass { public override System.Int32 Add(System.Int32 i,System.Int32 j) {System.Int32 r = default; r = base.Add(i,j); r++; return r; }}Copy the code

2.3 Test them together

public static class TestProxyJit
{
    public static RealClass GenerateRealClassProxy()
    {
        var generator = new ProxyGenerator();
        var code = generator.GenerateProxyCode(typeof(RealClass), (sb, method) => { }, (sb, method) => { sb.Append("r++;"); });
        var jit = new Jit();
        var syntaxTree = jit.ParseToSyntaxTree(code);
        var compilation = jit.BuildCompilation(syntaxTree);
        var assembly = jit.ComplieToAssembly(compilation);
        return Activator.CreateInstance(assembly.GetTypes().First()) as RealClass;
    }
    public static void Test()
    {
        RealClass proxy = GenerateRealClassProxy();
        var i = 5;
        var j = 10;
        Console.WriteLine($"{i} + {j} = {(i + j)}, but proxy is {proxy.Add(i, j)}");
    }
}
Copy the code

The result is:

5 + 10 = 15, but proxy is 16
Copy the code



Yeah, we wrote all this code to make 15 16, so no one would know there was an r++; That is what AOP is all about

The full demo is available at github.com/fs7744/AopD…

2.4 And then perfect it… (Just a few more years)

You just need to improve as follows:

  • Support for void methods
  • Support async await methods
  • Support for Abstract classes
  • Support interface
  • Support constructor
  • Support properties
  • Support indexer
  • Support in out ref
  • Supports generics
  • Support for nested classes
  • Support the remaining variety of cases

Well, believe in yourself. You can do it

3. It’s not recommended to play in a production environment without optimization, why?

3.1 two pictures

Handwritten proxy:



Jit-compiled proxy:

As the number of Proxy classes that need to be compiled increases, both CPU and memory will increase, so it is better to use some optimized solutions, such as github.com/dotnetcore/…

3.2 Do you trust the person calling your API

Well, this is a trust issue, so don’t call Roslyn SDK input exposed without checking, hackers will never run out of operations

The Roslyn SDK API will not be called as long as the other party can be trusted

But we all know there’s a rule we’ve been told before: Don’t trust any Input.

So everyone’s cat owner will follow others to run away depends on everyone’s confidence and means.