|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using WasmAppBuilder;
internal static class SignatureMapper
{
internal static char? TypeToChar(Type t, LogAdapter log, out bool isByRefStruct, int depth = 0)
{
isByRefStruct = false;
if (depth > 5) {
log.Warning("WASM0064", $"Unbounded recursion detected through parameter type '{t.Name}'");
return null;
}
char? c = null;
if (t.Namespace == "System") {
c = t.Name switch
{
nameof(String) => 'I',
nameof(Boolean) => 'I',
nameof(Char) => 'I',
nameof(SByte) => 'I',
nameof(Byte) => 'I',
nameof(Int16) => 'I',
nameof(UInt16) => 'I',
nameof(Int32) => 'I',
nameof(UInt32) => 'I',
nameof(Int64) => 'L',
nameof(UInt64) => 'L',
nameof(Single) => 'F',
nameof(Double) => 'D',
// FIXME: These will need to be L for wasm64
nameof(IntPtr) => 'I',
nameof(UIntPtr) => 'I',
"Void" => 'V',
_ => null
};
}
if (c == null)
{
// FIXME: Most of these need to be L for wasm64
if (t.IsArray)
c = 'I';
else if (t.IsByRef)
c = 'I';
else if (typeof(Delegate).IsAssignableFrom(t))
// FIXME: Should we narrow this to only certain types of delegates?
c = 'I';
else if (t.IsClass)
c = 'I';
else if (t.IsInterface)
c = 'I';
else if (t.IsEnum) {
Type underlyingType = t.GetEnumUnderlyingType();
c = TypeToChar(underlyingType, log, out _, ++depth);
} else if (t.IsPointer)
c = 'I';
else if (PInvokeTableGenerator.IsFunctionPointer(t))
c = 'I';
else if (t.IsValueType)
{
var fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fields.Length == 1) {
Type fieldType = fields[0].FieldType;
return TypeToChar(fieldType, log, out isByRefStruct, ++depth);
} else if (PInvokeTableGenerator.IsBlittable(t, log))
c = 'I';
isByRefStruct = true;
}
else
log.Warning("WASM0064", $"Unsupported parameter type '{t.Name}'");
}
return c;
}
public static string? MethodToSignature(MethodInfo method, LogAdapter log)
{
string? result = TypeToChar(method.ReturnType, log, out bool resultIsByRef)?.ToString();
if (result == null)
{
return null;
}
if (resultIsByRef) {
// WASM abi passes a result-pointer in slot 0 instead of returning struct results
result = "VI";
}
foreach (var parameter in method.GetParameters())
{
char? parameterChar = TypeToChar(parameter.ParameterType, log, out _);
if (parameterChar == null)
{
return null;
}
result += parameterChar;
}
return result;
}
public static string CharToNativeType(char c) => c switch
{
'V' => "void",
'I' => "int",
'L' => "int64_t",
'F' => "float",
'D' => "double",
_ => throw new InvalidSignatureCharException(c)
};
public static bool IsVoidSignature(string signature) => signature[0] == 'V';
}
internal sealed class InvalidSignatureCharException : Exception
{
public char Char { get; private set; }
public InvalidSignatureCharException(char c) : base($"Can't handle signature '{c}'") => Char = c;
}
|