0

I want to get the approximate size of a struct that is passed as a parameter to a method. I am using a Roslyn analyzer. We want to check that structs passed in by value are not larger than 128 bytes. The use of sizeof or Marshal.SizeOf is sufficient for our purposes.

The problem is that from what I understand, I cannot use reflection to get the type in the analyzer since reflection is a runtime API. Is there any way to circumvent this issue? Or to get the approximate size of the struct from the information available in Roslyn? Here are some things I have tried so far:

private static void CheckMethodParameters(SyntaxNodeAnalysisContext context)
{
    var methodDeclarationSyntax = (MethodDeclarationSyntax)context.Node;

    foreach (var parameterSyntax in methodDeclarationSyntax.ParameterList.Parameters)
    {
        var parameterSymbol = context.SemanticModel.GetDeclaredSymbol(parameterSyntax);
        if (parameterSymbol == null)
            continue;

        // As expected, none of these have the type I need 
        var entryAssembly = Assembly.GetEntryAssembly();
        var callingAssembly = Assembly.GetCallingAssembly();
        var executingAssemblyAssembly = Assembly.GetExecutingAssembly();

        // This only gets the NamedTypeSymbol, not the type
        var namedTypeSymbol = context.Compilation.GetTypeByMetadataName($"{parameterSymbol.Type.ContainingNamespace}.{parameterSymbol.Type.Name}");

        // None of these work
        var typeFromFullName = Type.GetType($"{parameterSymbol.Type.ContainingNamespace}.{parameterSymbol.Type.Name}, {context.Compilation.Assembly}");
        var assembly = Assembly.Load(context.Compilation.Assembly.ToString());
        var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
        var type = Assembly.Load(context.Compilation.Assembly.Name).GetType($"{parameterSymbol.Type.ContainingNamespace}.{parameterSymbol.Type.Name}");
    }
}

Any help would be greatly appreciated.

Tomáš Oplatek
  • 220
  • 3
  • 10

1 Answers1

1

The use of sizeof or Marshal.SizeOf is sufficient for our purposes

Marshal.SizeOf does an extremely complicated job of mapping a native buffer to a set of nested managed classes. If you need to use Marshal, your structure isn't blittable (for example, it won't conform to a where T : unmanaged restriction).

So, you need to decide whether you need that functionality in the first place.

That said, there's only so many blittable types in C# to begin with, you can easily write your own size checking code. You have all the basic numeric value types (int, double, etc) which all have standard sizes no matter what, then you have nint and nuint which depend on your bitness, and so on.

Then you have fixed arrays of basic value types (fixed int arr[10]), which should also be trivial to calculate.

If you do need the marshaller to support strings, you can use the attributes on them (CharSet and SizeConst) to calculate their fixed size as well.

Between all of those you should be able to cover almost all native structures out there. There's no code done for you to do all this, but it's really not complicated.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • The problem is that the struct can have fields of a different type of struct, so I would need to calculate those recursively as well, which starts being more trouble than it is worth honestly. – Tomáš Oplatek Aug 04 '21 at 15:54
  • 1
    Why would that be a problem? As long as you can calculate the size of a struct, it's trivial to call it recursively as needed. You won't get very far with something as complex as code analysis if calling one function is the difference between doing something and not doing it! – Blindy Aug 04 '21 at 16:01