Cysharp / MemoryPack

Zero encoding extreme performance binary serializer for C# and Unity.
MIT License
3.25k stars 190 forks source link

Run In Roslyn #303

Closed SunSi12138 closed 2 months ago

SunSi12138 commented 3 months ago

Finnaly I found the way to call source generator in roslyn at runtime mentioned in #285

But it will throw exception like this

Compile Failed
The constraints for type parameter 'TBufferWriter' of method 'Model.Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>, scoped ref Model?)' must match the constraints for type parameter 'TBufferWriter' of interface method 'IMemoryPackable<Model>.Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>, scoped ref Model?)'. Consider using an explicit interface implementation instead.
The type 'TBufferWriter' must be a reference type in order to use it as parameter 'TBufferWriter' in the generic type or method 'Model.Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter>, scoped ref Model?)'

what happed here?
code example:


using MemoryPack;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Reflection;
using System.Runtime.Loader;
using Test;

var sources = new List<(string Name,string Code)>();
sources.Add(("Model",
@"using MemoryPack;
namespace Test;
[MemoryPackable]
public partial class Model
{
    public string Name { get; set; }
}
"));
List<string> references =
[
    CodeCompiler.FromName("mscorlib"), 
    CodeCompiler.FromName("System"), 
    CodeCompiler.FromName("System.Core"), 
    CodeCompiler.FromName("System.Runtime"),
    CodeCompiler.FromName("System.Memory"),
    CodeCompiler.FromName("System.Private.CoreLib"),
    CodeCompiler.FromName("System.Private.Uri"),
    CodeCompiler.FromName("System.Runtime.Numerics"),
    CodeCompiler.FromName("System.Collections"),
    AppContext.BaseDirectory +"MemoryPack.Core.dll"
];
// Need To Copy this dll to output directory
List<string> analyzers = [AppContext.BaseDirectory +"MemoryPack.Generator.dll"];

var d = await CodeCompiler.CompileSources(AppContext.BaseDirectory +"output.dll",references,analyzers,sources,false);

if(d.Success)
{
    Console.WriteLine("Compile Success");
}
else
{
    Console.WriteLine("Compile Failed");
    foreach(var error in d.Errors)
    {
        Console.WriteLine(error);
    }
    return;
}

var assembly = Assembly.LoadFile(AppContext.BaseDirectory + "output.dll");

var type = assembly.GetType("Test.Model");
// var type = typeof(Model);
var instance = Activator.CreateInstance(type);
type.GetProperty("Name").SetValue(instance,"Hello World");

var bytes = MemoryPackSerializer.Serialize<Model>(instance as Model);
Console.WriteLine(bytes.Length);

public static class CodeCompiler
{
    static readonly string RuntimePath = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
    public static string FromName(string name)=>$"{RuntimePath}{name}.dll";
    const string PROJECT_NAME = "VisualSharpOutput";
    const string ASSEMBLY_NAME = "VisualSharpOutput";
    public static async Task<CodeCompilationResult> CompileSources(string outputPath, IEnumerable<string> assemblyPaths, IEnumerable<string> analyzersPaths,
        IEnumerable<(string Name,string Code)> sources, bool generateExecutable)
    {
        var projectInfo = ProjectInfo.Create(
            ProjectId.CreateNewId(),
            VersionStamp.Create(),
            PROJECT_NAME,
            ASSEMBLY_NAME,
            LanguageNames.CSharp
        );

        var solution = new AdhocWorkspace()
            .CurrentSolution
            .AddProject(projectInfo);

        var project = solution.GetProject(projectInfo.Id);
        var compilationOptions = new CSharpCompilationOptions(generateExecutable ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary);
        project = project.WithCompilationOptions(compilationOptions);

        IEnumerable<MetadataReference> references = assemblyPaths.Select(path => MetadataReference.CreateFromFile(path));
        project = project.AddMetadataReferences(references);
        IEnumerable<AnalyzerFileReference> analyzers = analyzersPaths.Select(path => new AnalyzerFileReference(path, new SimpleAnalyzerAssemblyLoader()));
        project = project.AddAnalyzerReferences(analyzers);
        foreach(var source in sources)
        {
            project = project.AddDocument($"{source.Name}.cs", source.Code).Project;
        }

        // Create compilation
        var compilation = await project.GetCompilationAsync();
        var analyzerReferences = project.AnalyzerReferences;
        var generators = analyzerReferences.SelectMany(reference => reference.GetGeneratorsForAllLanguages());
        var generator =  generators.First().GetType();

        GeneratorDriver driver = CSharpGeneratorDriver.Create(
            generators:generators,
            driverOptions: new GeneratorDriverOptions(default,trackIncrementalGeneratorSteps:true)
        );
        driver = driver.RunGenerators(compilation);

        var emitResult = compilation.Emit(outputPath);
        // EmitResult emitResult = updatedCompilation.Emit(outputPath);

        IEnumerable<string> errors = emitResult.Diagnostics
            .Where(d => d.Severity == DiagnosticSeverity.Error)
            .Select(d => d.GetMessage());

        return new CodeCompilationResult(emitResult.Success, errors, emitResult.Success ? outputPath : "");
    }
}
public class CodeCompilationResult(bool success, IEnumerable<string> errors,string pathToAssembly)
{
    public bool Success=>success;

    public IEnumerable<string> Errors=>errors;

    public string PathToAssembly=>pathToAssembly;
}
public class SimpleAnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
{
    static AssemblyLoadContext _loadContext = new("SimpleAnalyzerAssemblyLoader");
    public void AddDependencyLocation(string fullPath)
    {
    }

    public Assembly LoadFromPath(string fullPath)=>_loadContext.LoadFromAssemblyPath(fullPath);
}

namespace Test
{
    [MemoryPackable]
    public partial class Model
    {
        public string Name { get; set; }
    }
}
neuecc commented 2 months ago

I don't know.