Skip to content

Commit 9638864

Browse files
committed
Core: Fix wasm signature calculation (hopefully for good?)
1 parent b7afe78 commit 9638864

2 files changed

Lines changed: 13 additions & 8 deletions

File tree

Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context,
2020

2121
if (wasmDef == null)
2222
{
23-
Logger.WarnNewline($"Could not find WASM definition for method {methodDefinition.HumanReadableSignature} in {methodDefinition.DeclaringType?.FullName}, probably incorrect signature calculation (signature was {WasmUtils.BuildSignature(context)})", "WasmInstructionSet");
23+
Logger.WarnNewline($"Could not find WASM definition for method {methodDefinition.HumanReadableSignature} in {methodDefinition.DeclaringType?.FullName}, probably incorrect signature calculation (signature was {WasmUtils.BuildSignature(context)}, index is {context.UnderlyingPointer})", "WasmInstructionSet");
2424
return Array.Empty<byte>();
2525
}
2626

2727
if (wasmDef.AssociatedFunctionBody == null)
28-
throw new($"WASM definition {wasmDef}, resolved from MethodAnalysisContext {context.Definition.HumanReadableSignature} in {context.DeclaringType?.FullName} has no associated function body (signature was {WasmUtils.BuildSignature(context)})");
28+
throw new($"WASM definition {wasmDef}, resolved from MethodAnalysisContext {context.Definition.HumanReadableSignature} in {context.DeclaringType?.FullName} has no associated function body (signature was {WasmUtils.BuildSignature(context)}, index is {context.UnderlyingPointer})");
2929

3030
return wasmDef.AssociatedFunctionBody.Instructions;
3131
}

Cpp2IL.Core/Utils/WasmUtils.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ public static string BuildSignature(MethodAnalysisContext definition)
3232
: definition.ReturnType switch
3333
{
3434
{ Namespace: nameof(System), Name: "Void" } => "v",
35-
{ IsValueType: true, Definition: null or { Size: < 0 or > 8 } } => "vi", //Large or Generic Struct returns have a void return type, but the actual return value is the first parameter.
36-
{ IsValueType: true, Definition.Size: > 4 } => "j", //Medium structs are returned as longs
37-
{ IsValueType: true, Definition.Size: <= 4 } => "i", //Small structs are returned as ints
38-
_ => GetSignatureLetter(definition.ReturnType!)
35+
{ IsValueType: true, Definition: null or { Size: > 8 } } => "vi", //Large or Generic Struct returns have a void return type, but the actual return value is the first parameter.
36+
{ IsValueType: true, Definition.Size: > 0 and < 4 } => "i", //Small structs are returned as ints
37+
_ => GetSignatureLetter(definition.ReturnType!, forReturn: true)
3938
};
4039

4140
return $"{returnTypeSignature}{instanceParam}{string.Join("", definition.Parameters!.Select(p => GetSignatureLetter(p.ParameterType, p.IsRef)))}i"; //Add an extra i on the end for the method info param
@@ -49,7 +48,7 @@ public static bool IsWasmPrimitive(this TypeAnalysisContext type)
4948
return typeEnum is >= Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN and <= Il2CppTypeEnum.IL2CPP_TYPE_R8;
5049
}
5150

52-
private static string GetSignatureLetter(TypeAnalysisContext type, bool isRefOrOut = false)
51+
private static string GetSignatureLetter(TypeAnalysisContext type, bool isRefOrOut = false, bool forReturn = false)
5352
{
5453
if (isRefOrOut)
5554
//ref/out params are passed as pointers
@@ -62,6 +61,13 @@ private static string GetSignatureLetter(TypeAnalysisContext type, bool isRefOrO
6261
if (type.IsEnumType)
6362
type = type.EnumUnderlyingType ?? throw new($"Enum type {type} has no underlying type");
6463

64+
if (type.IsValueType && type is { Namespace: nameof(System), Name: nameof(DateTime) or nameof(TimeSpan) })
65+
return "j"; //gross hardcoding but i think this is literally the only 2 cases?
66+
67+
if(forReturn && type.IsValueType && type.Fields.Count(f => !f.IsStatic && (f.Attributes & FieldAttributes.Literal) == 0) > 1)
68+
//this seems to work in my testing, at least
69+
return "vi";
70+
6571
// var typeDefinition = type.BaseType ?? type.AppContext.SystemTypes.SystemInt32Type;
6672

6773
return type.Name switch
@@ -71,7 +77,6 @@ private static string GetSignatureLetter(TypeAnalysisContext type, bool isRefOrO
7177
"Single" => "f",
7278
"Double" => "d",
7379
"Int32" => "i",
74-
_ when !type.IsWasmPrimitive() && type is { IsValueType: true, IsEnumType: false, Definition.Size: <= 8 and > 0 } => "j", //TODO check - value types < 16 bytes (including base object header which is irrelevant here) are passed directly as long?
7580
_ => "i"
7681
};
7782
}

0 commit comments

Comments
 (0)