diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c
index 484eedb9038f1..8e064696fe762 100644
--- a/src/mono/mono/metadata/class-init.c
+++ b/src/mono/mono/metadata/class-init.c
@@ -2272,6 +2272,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
}
size = mono_type_size (field->type, &align);
+ // keep in sync with marshal.c mono_marshal_load_type_info
if (m_class_is_inlinearray (klass)) {
// Limit the max size of array instance to 1MiB
const guint32 struct_max_size = 1024 * 1024;
diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c
index 9c3ea1171106e..3be82b80e03af 100644
--- a/src/mono/mono/metadata/icall.c
+++ b/src/mono/mono/metadata/icall.c
@@ -3028,7 +3028,7 @@ ves_icall_RuntimeType_GetNamespace (MonoQCallTypeHandle type_handle, MonoObjectH
MonoClass *klass = mono_class_from_mono_type_internal (type);
MonoClass *elem;
- while (!m_class_is_enumtype (klass) &&
+ while (!m_class_is_enumtype (klass) &&
!mono_class_is_nullable (klass) &&
(klass != (elem = m_class_get_element_class (klass))))
klass = elem;
diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c
index c57fa32127a9f..1b7d0b4310a2b 100644
--- a/src/mono/mono/metadata/marshal.c
+++ b/src/mono/mono/metadata/marshal.c
@@ -3294,7 +3294,7 @@ mono_emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t,
return mono_emit_disabled_marshal (m, argnum, t, spec, conv_arg, conv_arg_type, action);
return mono_component_marshal_ilgen()->emit_marshal_ilgen(m, argnum, t, spec, conv_arg, conv_arg_type, action, get_marshal_cb());
-}
+}
static void
mono_marshal_set_callconv_for_type(MonoType *type, MonoMethodSignature *csig, gboolean *skip_gc_trans /*out*/)
@@ -3577,7 +3577,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) {
/*
- * In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation.
+ * In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation.
* Emit a wrapper that throws a NotSupportedException.
*/
get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute");
@@ -3757,7 +3757,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
}
goto leave;
-
+
emit_exception_for_error:
mono_error_cleanup (emitted_error);
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
@@ -5231,7 +5231,7 @@ mono_marshal_get_unsafe_accessor_wrapper (MonoMethod *accessor_method, MonoUnsaf
if (member_name == NULL && kind != MONO_UNSAFE_ACCESSOR_CTOR)
member_name = accessor_method->name;
-
+
/*
* Check cache
*/
@@ -5827,11 +5827,20 @@ mono_marshal_load_type_info (MonoClass* klass)
continue;
}
+ size = mono_marshal_type_size (field->type, info->fields [j].mspec,
+ &align, TRUE, m_class_is_unicode (klass));
+
+ // Keep in sync with class-init.c mono_class_layout_fields
+ if (m_class_is_inlinearray (klass)) {
+ // Limit the max size of array instance to 1MiB
+ const int struct_max_size = 1024 * 1024;
+ size *= m_class_inlinearray_value (klass);
+ g_assert ((size > 0) && (size <= struct_max_size));
+ }
+
switch (layout) {
case TYPE_ATTRIBUTE_AUTO_LAYOUT:
case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT:
- size = mono_marshal_type_size (field->type, info->fields [j].mspec,
- &align, TRUE, m_class_is_unicode (klass));
align = m_class_get_packing_size (klass) ? MIN (m_class_get_packing_size (klass), align): align;
min_align = MAX (align, min_align);
info->fields [j].offset = info->native_size;
@@ -5840,8 +5849,6 @@ mono_marshal_load_type_info (MonoClass* klass)
info->native_size = info->fields [j].offset + size;
break;
case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT:
- size = mono_marshal_type_size (field->type, info->fields [j].mspec,
- &align, TRUE, m_class_is_unicode (klass));
min_align = MAX (align, min_align);
info->fields [j].offset = m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject);
info->native_size = MAX (info->native_size, info->fields [j].offset + size);
diff --git a/src/mono/mono/mini/aot-runtime-wasm.c b/src/mono/mono/mini/aot-runtime-wasm.c
index cf1ab02392934..30fde73c155bd 100644
--- a/src/mono/mono/mini/aot-runtime-wasm.c
+++ b/src/mono/mono/mini/aot-runtime-wasm.c
@@ -15,8 +15,12 @@
#ifdef HOST_WASM
static char
-type_to_c (MonoType *t)
+type_to_c (MonoType *t, gboolean *is_byref_return)
{
+ g_assert (t);
+
+ if (is_byref_return)
+ *is_byref_return = 0;
if (m_type_is_byref (t))
return 'I';
@@ -48,7 +52,7 @@ type_to_c (MonoType *t)
return 'L';
case MONO_TYPE_VOID:
return 'V';
- case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_VALUETYPE: {
if (m_class_is_enumtype (t->data.klass)) {
t = mono_class_enum_basetype_internal (t->data.klass);
goto handle_enum;
@@ -60,13 +64,27 @@ type_to_c (MonoType *t)
// FIXME: Handle the scenario where there are fields of struct types that contain no members
MonoType *scalar_vtype;
if (mini_wasm_is_scalar_vtype (t, &scalar_vtype))
- return type_to_c (scalar_vtype);
+ return type_to_c (scalar_vtype, NULL);
+
+ if (is_byref_return)
+ *is_byref_return = 1;
return 'I';
- case MONO_TYPE_GENERICINST:
- if (m_class_is_valuetype (t->data.klass))
+ }
+ case MONO_TYPE_GENERICINST: {
+ if (m_class_is_valuetype (t->data.klass)) {
+ MonoType *scalar_vtype;
+ if (mini_wasm_is_scalar_vtype (t, &scalar_vtype))
+ return type_to_c (scalar_vtype, NULL);
+
+ if (is_byref_return)
+ *is_byref_return = 1;
+
return 'S';
+ }
+
return 'I';
+ }
default:
g_warning ("CANT TRANSLATE %s", mono_type_full_name (t));
return 'X';
@@ -140,18 +158,29 @@ gpointer
mono_wasm_get_interp_to_native_trampoline (MonoMethodSignature *sig)
{
char cookie [32];
- int c_count;
+ int c_count, offset = 1;
+ gboolean is_byref_return = 0;
+
+ memset (cookie, 0, 32);
+ cookie [0] = type_to_c (sig->ret, &is_byref_return);
- c_count = sig->param_count + sig->hasthis + 1;
+ c_count = sig->param_count + sig->hasthis + is_byref_return + 1;
g_assert (c_count < sizeof (cookie)); //ensure we don't overflow the local
- cookie [0] = type_to_c (sig->ret);
- if (sig->hasthis)
- cookie [1] = 'I';
+ if (is_byref_return) {
+ cookie[0] = 'V';
+ // return value address goes in arg0
+ cookie[1] = 'I';
+ offset += 1;
+ }
+ if (sig->hasthis) {
+ // thisptr goes in arg0/arg1 depending on return type
+ cookie [offset] = 'I';
+ offset += 1;
+ }
for (int i = 0; i < sig->param_count; ++i) {
- cookie [1 + sig->hasthis + i] = type_to_c (sig->params [i]);
+ cookie [offset + i] = type_to_c (sig->params [i], NULL);
}
- cookie [c_count] = 0;
void *p = mono_wasm_interp_to_native_callback (cookie);
if (!p)
diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c
index 411d4f1e6b3db..c16afc494011f 100644
--- a/src/mono/mono/mini/interp/interp.c
+++ b/src/mono/mono/mini/interp/interp.c
@@ -1340,7 +1340,10 @@ typedef enum {
PINVOKE_ARG_R8 = 3,
PINVOKE_ARG_R4 = 4,
PINVOKE_ARG_VTYPE = 5,
- PINVOKE_ARG_SCALAR_VTYPE = 6
+ PINVOKE_ARG_SCALAR_VTYPE = 6,
+ // This isn't ifdefed so it's easier to write code that handles it without sprinkling
+ // 800 ifdefs in this file
+ PINVOKE_ARG_WASM_VALUETYPE_RESULT = 7,
} PInvokeArgType;
typedef struct {
@@ -1436,6 +1439,7 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur
ilen++;
break;
case MONO_TYPE_GENERICINST: {
+ // FIXME: Should mini_wasm_is_scalar_vtype stuff go in here?
MonoClass *container_class = type->data.generic_class->container_class;
type = m_class_get_byval_arg (container_class);
goto retry;
@@ -1473,11 +1477,32 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur
case MONO_TYPE_CLASS:
case MONO_TYPE_OBJECT:
case MONO_TYPE_STRING:
+ info->ret_pinvoke_type = PINVOKE_ARG_INT;
+ break;
+#if SIZEOF_VOID_P == 8
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+#endif
+ info->ret_pinvoke_type = PINVOKE_ARG_INT;
+ break;
+#if SIZEOF_VOID_P == 4
case MONO_TYPE_I8:
case MONO_TYPE_U8:
+ info->ret_pinvoke_type = PINVOKE_ARG_INT;
+ break;
+#endif
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_GENERICINST:
info->ret_pinvoke_type = PINVOKE_ARG_INT;
+#ifdef HOST_WASM
+ // This ISSTRUCT check is important, because the type could be an enum
+ if (MONO_TYPE_ISSTRUCT (info->ret_mono_type)) {
+ // The return type was already filtered previously, so if we get here
+ // we're returning a struct byref instead of as a scalar
+ info->ret_pinvoke_type = PINVOKE_ARG_WASM_VALUETYPE_RESULT;
+ info->ilen++;
+ }
+#endif
break;
case MONO_TYPE_R4:
case MONO_TYPE_R8:
@@ -1503,6 +1528,15 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui
margs->ilen = info->ilen;
margs->flen = info->flen;
+ size_t int_i = 0;
+ size_t int_f = 0;
+
+ if (info->ret_pinvoke_type == PINVOKE_ARG_WASM_VALUETYPE_RESULT) {
+ // Allocate an empty arg0 for the address of the return value
+ // info->ilen was already increased earlier
+ int_i++;
+ }
+
if (margs->ilen > 0) {
if (margs->ilen <= 8)
margs->iargs = margs->iargs_buf;
@@ -1517,9 +1551,6 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui
margs->fargs = g_malloc0 (sizeof (double) * margs->flen);
}
- size_t int_i = 0;
- size_t int_f = 0;
-
for (int i = 0; i < sig->param_count; i++) {
guint32 offset = get_arg_offset (frame->imethod, sig, i);
stackval *sp_arg = STACK_ADD_BYTES (frame->stack, offset);
@@ -1578,6 +1609,15 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui
}
switch (info->ret_pinvoke_type) {
+ case PINVOKE_ARG_WASM_VALUETYPE_RESULT:
+ // We pass the return value address in arg0 so fill it in, we already
+ // reserved space for it earlier.
+ g_assert (frame->retval);
+ margs->iargs[0] = (gpointer*)frame->retval;
+ // The return type is void so retval should be NULL
+ margs->retval = NULL;
+ margs->is_float_ret = 0;
+ break;
case PINVOKE_ARG_INT:
margs->retval = (gpointer*)frame->retval;
margs->is_float_ret = 0;
@@ -1795,8 +1835,10 @@ ves_pinvoke_method (
g_free (ccontext.stack);
#else
// Only the vt address has been returned, we need to copy the entire content on interp stack
- if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type))
- stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled);
+ if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type)) {
+ if (call_info->ret_pinvoke_type != PINVOKE_ARG_WASM_VALUETYPE_RESULT)
+ stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled);
+ }
if (margs.iargs != margs.iargs_buf)
g_free (margs.iargs);
diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c
index 0a80ab43ba9c4..991135288f63d 100644
--- a/src/mono/mono/mini/mini-wasm.c
+++ b/src/mono/mono/mini/mini-wasm.c
@@ -75,17 +75,23 @@ get_storage (MonoType *type, MonoType **etype, gboolean is_return)
case MONO_TYPE_R8:
return ArgOnStack;
- case MONO_TYPE_GENERICINST:
+ case MONO_TYPE_GENERICINST: {
if (!mono_type_generic_inst_is_valuetype (type))
return ArgOnStack;
if (mini_is_gsharedvt_variable_type (type))
return ArgGsharedVTOnStack;
- /* fall through */
+
+ if (mini_wasm_is_scalar_vtype (type, etype))
+ return ArgVtypeAsScalar;
+
+ return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack;
+ }
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_TYPEDBYREF: {
if (mini_wasm_is_scalar_vtype (type, etype))
return ArgVtypeAsScalar;
+
return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack;
}
case MONO_TYPE_VAR:
@@ -771,7 +777,12 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype)
if (nfields > 1)
return FALSE;
MonoType *t = mini_get_underlying_type (field->type);
- if (MONO_TYPE_ISSTRUCT (t)) {
+ int align, field_size = mono_type_size (t, &align);
+ // inlinearray and fixed both work by having a single field that is bigger than its element type.
+ // we also don't want to scalarize a struct that has padding in its metadata, even if it would fit.
+ if (field_size != size) {
+ return FALSE;
+ } else if (MONO_TYPE_ISSTRUCT (t)) {
if (!mini_wasm_is_scalar_vtype (t, etype))
return FALSE;
} else if (!((MONO_TYPE_IS_PRIMITIVE (t) || MONO_TYPE_IS_REFERENCE (t) || MONO_TYPE_IS_POINTER (t)))) {
diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
index 1d7a9740eba9e..8c0442a1b0d68 100644
--- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
@@ -739,6 +739,16 @@ public struct Nested1 {
public struct SingleI64Struct {
public Int64 Value;
}
+ public struct PairStruct {
+ public int A, B;
+ }
+ public unsafe struct MyFixedArray {
+ public fixed int elements[2];
+ }
+ [System.Runtime.CompilerServices.InlineArray(2)]
+ public struct MyInlineArray {
+ public int element0;
+ }
public class Test
{
@@ -765,9 +775,35 @@ public static unsafe int Main(string[] argv)
var res = indirect(sds);
Console.WriteLine(""s (s)="" + res.Value);
+ var pair = new PairStruct { A = 1, B = 2 };
+ var paires = accept_and_return_pair(pair);
+ Console.WriteLine(""paires.B="" + paires.B);
+
+ // This test is split into methods to simplify debugging issues with it
+ var ia = InlineArrayTest1();
+ var iares = InlineArrayTest2(ia);
+ Console.WriteLine($""iares[0]={iares[0]} iares[1]={iares[1]}"");
+
+ MyFixedArray fa = new ();
+ for (int i = 0; i < 2; i++)
+ fa.elements[i] = i;
+ var fares = accept_and_return_fixedarray(fa);
+ Console.WriteLine(""fares.elements[1]="" + fares.elements[1]);
+
return (int)res.Value;
}
+ public static unsafe MyInlineArray InlineArrayTest1 () {
+ MyInlineArray ia = new ();
+ for (int i = 0; i < 2; i++)
+ ia[i] = i;
+ return ia;
+ }
+
+ public static unsafe MyInlineArray InlineArrayTest2 (MyInlineArray ia) {
+ return accept_and_return_inlinearray(ia);
+ }
+
[DllImport(""wasm-abi"", EntryPoint=""accept_double_struct_and_return_float_struct"")]
public static extern SingleFloatStruct indirect(SingleDoubleStruct arg);
@@ -782,9 +818,18 @@ public static unsafe int Main(string[] argv)
[DllImport(""wasm-abi"", EntryPoint=""accept_and_return_i64_struct"")]
public static extern Int64 direct64(Int64 arg);
+
+ [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_pair"")]
+ public static extern PairStruct accept_and_return_pair(PairStruct arg);
+
+ [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_fixedarray"")]
+ public static extern MyFixedArray accept_and_return_fixedarray(MyFixedArray arg);
+
+ [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_inlinearray"")]
+ public static extern MyInlineArray accept_and_return_inlinearray(MyInlineArray arg);
}";
- var extraProperties = "true<_WasmDevel>true";
+ var extraProperties = "true<_WasmDevel>falsefalse";
var extraItems = @"";
buildArgs = ExpandBuildArgs(buildArgs,
@@ -824,6 +869,10 @@ public static unsafe int Main(string[] argv)
Assert.Contains("f (d)=3.14", runOutput);
Assert.Contains("f (s)=3.14", runOutput);
Assert.Contains("s (s)=3.14", runOutput);
+ Assert.Contains("paires.B=4", runOutput);
+ Assert.Contains("iares[0]=32", runOutput);
+ Assert.Contains("iares[1]=2", runOutput);
+ Assert.Contains("fares.elements[1]=2", runOutput);
}
[Theory]
diff --git a/src/mono/wasm/testassets/native-libs/wasm-abi.c b/src/mono/wasm/testassets/native-libs/wasm-abi.c
index 0ace2037daf2f..083bce6abe0c5 100644
--- a/src/mono/wasm/testassets/native-libs/wasm-abi.c
+++ b/src/mono/wasm/testassets/native-libs/wasm-abi.c
@@ -1,5 +1,7 @@
#include
+#define TRACING 0
+
typedef struct {
float value;
} TRes;
@@ -7,10 +9,12 @@ typedef struct {
TRes accept_double_struct_and_return_float_struct (
struct { struct { double value; } value; } arg
) {
+#if TRACING
printf (
"&arg=%x (ulonglong)arg=%llx arg.value.value=%lf\n",
(unsigned int)&arg, *(unsigned long long*)&arg, (double)arg.value.value
);
+#endif
TRes result = { arg.value.value };
return result;
}
@@ -20,10 +24,48 @@ typedef struct {
} TResI64;
TResI64 accept_and_return_i64_struct (TResI64 arg) {
+#if TRACING
printf (
"&arg=%x (ulonglong)arg=%llx\n",
(unsigned int)&arg, *(unsigned long long*)&arg
);
+#endif
TResI64 result = { ~arg.value };
return result;
}
+
+typedef struct {
+ int A, B;
+} PairStruct;
+
+PairStruct accept_and_return_pair (PairStruct arg) {
+#if TRACING
+ printf (
+ "&arg=%d arg.A=%d arg.B=%d\n",
+ (unsigned int)&arg, arg.A, arg.B
+ );
+#endif
+ arg.A = 32;
+ arg.B *= 2;
+ return arg;
+}
+
+typedef struct {
+ int elements[2];
+} MyInlineArray;
+
+MyInlineArray accept_and_return_inlinearray (MyInlineArray arg) {
+#if TRACING
+ printf (
+ "&arg=%d arg.elements[0]=%d arg.elements[1]=%d\n",
+ (unsigned int)&arg, arg.elements[0], arg.elements[1]
+ );
+#endif
+ arg.elements[0] = 32;
+ arg.elements[1] *= 2;
+ return arg;
+}
+
+MyInlineArray accept_and_return_fixedarray (MyInlineArray arg) {
+ return accept_and_return_inlinearray (arg);
+}
diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
index cd8535463bc34..e4aa070d88ea3 100644
--- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
+++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
@@ -269,10 +269,19 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN
return null;
}
+ var realReturnType = method.ReturnType;
+ var realParameterTypes = method.GetParameters().Select(p => MapType(p.ParameterType)).ToList();
+
+ SignatureMapper.TypeToChar(realReturnType, Log, out bool resultIsByRef);
+ if (resultIsByRef) {
+ realReturnType = typeof(void);
+ realParameterTypes.Insert(0, "void *");
+ }
+
return
$$"""
{{(pinvoke.WasmLinkage ? $"__attribute__((import_module(\"{EscapeLiteral(pinvoke.Module)}\"),import_name(\"{EscapeLiteral(pinvoke.EntryPoint)}\")))" : "")}}
- {{(pinvoke.WasmLinkage ? "extern " : "")}}{{MapType(method.ReturnType)}} {{CEntryPoint(pinvoke)}} ({{string.Join(", ", method.GetParameters().Select(p => MapType(p.ParameterType)))}});
+ {{(pinvoke.WasmLinkage ? "extern " : "")}}{{MapType(realReturnType)}} {{CEntryPoint(pinvoke)}} ({{string.Join(", ", realParameterTypes)}});
""";
}
diff --git a/src/tasks/WasmAppBuilder/SignatureMapper.cs b/src/tasks/WasmAppBuilder/SignatureMapper.cs
index f3b7f17ad017b..3638e432f0ce3 100644
--- a/src/tasks/WasmAppBuilder/SignatureMapper.cs
+++ b/src/tasks/WasmAppBuilder/SignatureMapper.cs
@@ -11,8 +11,15 @@
internal static class SignatureMapper
{
- private static char? TypeToChar(Type t, LogAdapter log)
+ 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
@@ -20,6 +27,7 @@ internal static class SignatureMapper
nameof(String) => 'I',
nameof(Boolean) => 'I',
nameof(Char) => 'I',
+ nameof(SByte) => 'I',
nameof(Byte) => 'I',
nameof(Int16) => 'I',
nameof(UInt16) => 'I',
@@ -51,19 +59,23 @@ internal static class SignatureMapper
c = 'I';
else if (t.IsInterface)
c = 'I';
- else if (t.IsEnum)
- c = TypeToChar(t.GetEnumUnderlyingType(), log);
- else if (t.IsPointer)
+ 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)
- return TypeToChar(fields[0].FieldType, log);
- else if (PInvokeTableGenerator.IsBlittable(t, log))
+ 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}'");
@@ -74,15 +86,20 @@ internal static class SignatureMapper
public static string? MethodToSignature(MethodInfo method, LogAdapter log)
{
- string? result = TypeToChar(method.ReturnType, log)?.ToString();
+ 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);
+ char? parameterChar = TypeToChar(parameter.ParameterType, log, out _);
if (parameterChar == null)
{
return null;