Skip to content

Commit ce94d2d

Browse files
committed
Fix stackoverflow in Il2CppTypeHelper.Box<IObject>
1 parent 98e4072 commit ce94d2d

5 files changed

Lines changed: 139 additions & 4 deletions

File tree

Il2CppInterop.Generator/MarshallingProcessingLayer.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public override void Process(ApplicationAnalysisContext appContext, Action<int,
1717
var iil2CppType = appContext.ResolveTypeOrThrow(typeof(IIl2CppType));
1818
var iil2CppType_get_ObjectClass = iil2CppType.GetMethodByName($"get_{nameof(IIl2CppType.ObjectClass)}");
1919

20+
var iil2CppValueType = appContext.ResolveTypeOrThrow(typeof(IIl2CppValueType));
21+
var iil2CppValueType_get_Size = iil2CppValueType.GetMethodByName($"get_{nameof(IIl2CppValueType.Size)}");
22+
var iil2CppValueType_WriteToSpan = iil2CppValueType.GetMethodByName(nameof(IIl2CppValueType.WriteToSpan));
23+
2024
var iil2CppTypeGeneric = appContext.ResolveTypeOrThrow(typeof(IIl2CppType<>));
2125
var iil2CppTypeGeneric_get_Size = iil2CppTypeGeneric.GetMethodByName($"get_{nameof(IIl2CppType<>.Size)}");
2226
var iil2CppTypeGeneric_get_AssemblyName = iil2CppTypeGeneric.GetMethodByName($"get_{nameof(IIl2CppType<>.AssemblyName)}");
@@ -35,6 +39,7 @@ public override void Process(ApplicationAnalysisContext appContext, Action<int,
3539
var il2CppTypeHelper_WriteToSpanAtOffset = il2CppTypeHelper.GetMethodByName(nameof(Il2CppTypeHelper.WriteToSpanAtOffset));
3640
var il2CppTypeHelper_ReadFromSpanBlittable = il2CppTypeHelper.GetMethodByName(nameof(Il2CppTypeHelper.ReadFromSpanBlittable));
3741
var il2CppTypeHelper_WriteToSpanBlittable = il2CppTypeHelper.GetMethodByName(nameof(Il2CppTypeHelper.WriteToSpanBlittable));
42+
var il2CppTypeHelper_WriteToSpan = il2CppTypeHelper.GetMethodByName(nameof(Il2CppTypeHelper.WriteToSpan));
3843

3944
var intPtr_get_Size = appContext.SystemTypes.SystemIntPtrType.GetMethodByName($"get_{nameof(IntPtr.Size)}");
4045

@@ -58,6 +63,11 @@ public override void Process(ApplicationAnalysisContext appContext, Action<int,
5863
var instantiatedIl2CppTypeGeneric = iil2CppTypeGeneric.MakeGenericInstanceType([instantiatedType]);
5964
type.InterfaceContexts.Add(instantiatedIl2CppTypeGeneric);
6065

66+
if (type.IsValueType)
67+
{
68+
type.InterfaceContexts.Add(iil2CppValueType);
69+
}
70+
6171
TypeAnalysisContext nameReferenceType;
6272
TypeAnalysisContext classReferenceType;
6373
if (type == il2CppSystemIObject)
@@ -161,6 +171,46 @@ public override void Process(ApplicationAnalysisContext appContext, Action<int,
161171
IsInjected = true,
162172
};
163173
type.Properties.Add(property);
174+
175+
// IIl2CppValueType.Size
176+
if (type.IsValueType)
177+
{
178+
Debug.Assert(instantiatedSizeStorage is not null);
179+
var valueTypeMethodName = $"{iil2CppValueType.FullName}.get_{nameof(IIl2CppValueType.Size)}";
180+
var valueTypeMethod = new InjectedMethodAnalysisContext(
181+
type,
182+
valueTypeMethodName,
183+
iil2CppValueType_get_Size.ReturnType,
184+
MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName | MethodAttributes.NewSlot,
185+
[])
186+
{
187+
IsInjected = true,
188+
};
189+
type.Methods.Add(valueTypeMethod);
190+
valueTypeMethod.Overrides.Add(iil2CppValueType_get_Size);
191+
192+
valueTypeMethod.PutExtraData(new NativeMethodBody()
193+
{
194+
Instructions =
195+
[
196+
new Instruction(CilOpCodes.Ldsfld, instantiatedSizeStorage),
197+
new Instruction(CilOpCodes.Ret),
198+
],
199+
});
200+
201+
var valueTypePropertyName = $"{iil2CppValueType.FullName}.{nameof(IIl2CppValueType.Size)}";
202+
var valueTypeProperty = new InjectedPropertyAnalysisContext(
203+
valueTypePropertyName,
204+
valueTypeMethod.ReturnType,
205+
valueTypeMethod,
206+
null,
207+
PropertyAttributes.None,
208+
type)
209+
{
210+
IsInjected = true,
211+
};
212+
type.Properties.Add(valueTypeProperty);
213+
}
164214
}
165215

166216
// AssemblyName
@@ -457,6 +507,34 @@ public override void Process(ApplicationAnalysisContext appContext, Action<int,
457507
});
458508
}
459509
}
510+
// IIl2CppValueType.WriteToSpan
511+
if (type.IsValueType)
512+
{
513+
var methodName = $"{iil2CppValueType.FullName}.{nameof(IIl2CppValueType.WriteToSpan)}";
514+
var method = new InjectedMethodAnalysisContext(
515+
type,
516+
methodName,
517+
iil2CppValueType_WriteToSpan.ReturnType,
518+
MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot,
519+
iil2CppValueType_WriteToSpan.Parameters.Select(p => p.ParameterType).ToArray())
520+
{
521+
IsInjected = true,
522+
};
523+
type.Methods.Add(method);
524+
method.Overrides.Add(iil2CppValueType_WriteToSpan);
525+
526+
method.PutExtraData(new NativeMethodBody()
527+
{
528+
Instructions =
529+
[
530+
new Instruction(CilOpCodes.Ldarg_0),
531+
new Instruction(CilOpCodes.Ldobj, instantiatedType),
532+
new Instruction(CilOpCodes.Ldarg_1),
533+
new Instruction(CilOpCodes.Call, il2CppTypeHelper_WriteToSpan.MakeGenericInstanceMethod(instantiatedType)),
534+
new Instruction(CilOpCodes.Ret),
535+
],
536+
});
537+
}
460538
}
461539
}
462540
}

Il2CppInterop.Generator/ReferenceAssemblyInjectionProcessingLayer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public override void Process(ApplicationAnalysisContext appContext, Action<int,
5151
typeof(FieldAccessHelper),
5252
typeof(IIl2CppType),
5353
typeof(IIl2CppType<>),
54+
typeof(IIl2CppValueType),
5455
typeof(IIl2CppException),
5556
typeof(RuntimeInvokeHelper),
5657
typeof(Il2CppTypeHelper),

Il2CppInterop.Runtime/InteropTypes/IIl2CppType.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,32 @@ public interface IIl2CppType
88
}
99
public interface IIl2CppType<TSelf> : IIl2CppType where TSelf : notnull, IIl2CppType<TSelf>
1010
{
11+
/// <summary>
12+
/// The native size of the type in bytes
13+
/// </summary>
1114
static abstract int Size { get; }
15+
/// <summary>
16+
/// The name of the assembly that the type is defined in
17+
/// </summary>
1218
static virtual string AssemblyName => typeof(TSelf).Assembly.GetName().Name ?? "";
19+
/// <summary>
20+
/// The namespace of type
21+
/// </summary>
1322
static virtual string Namespace => typeof(TSelf).Namespace ?? "";
23+
/// <summary>
24+
/// The class name of the type
25+
/// </summary>
1426
static virtual string Name => typeof(TSelf).Name;
27+
/// <summary>
28+
/// Writes the native representation of the value to the provided span. The span is required to be at least <see cref="Size"/> bytes long.
29+
/// </summary>
30+
/// <param name="value">The value to write.</param>
31+
/// <param name="span">The span to write the value to.</param>
1532
static abstract void WriteToSpan(TSelf? value, Span<byte> span);
33+
/// <summary>
34+
/// Reads the native representation of the value from the provided span. The span is required to be at least <see cref="Size"/> bytes long.
35+
/// </summary>
36+
/// <param name="span">The span to read the value from.</param>
37+
/// <returns>The value read from the span.</returns>
1638
static abstract TSelf? ReadFromSpan(ReadOnlySpan<byte> span);
1739
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
3+
namespace Il2CppInterop.Runtime.InteropTypes;
4+
5+
public interface IIl2CppValueType : IIl2CppType
6+
{
7+
/// <summary>
8+
/// The native size of the type in bytes
9+
/// </summary>
10+
/// <remarks>
11+
/// This is not necessarily the same as <see cref="IIl2CppType{TSelf}.Size"/>.
12+
/// For example, it can differ when the object is boxed and the type argument is an interface.
13+
/// However, the implementation of this method simply returns the value provided by <see cref="IIl2CppType{TSelf}.Size"/> for the unboxed type.
14+
/// </remarks>
15+
int Size { get; }
16+
/// <summary>
17+
/// Writes the native representation of the value to the provided span. The span is required to be at least <see cref="Size"/> bytes long.
18+
/// </summary>
19+
/// <remarks>
20+
/// This is not necessarily the same as the method from <see cref="IIl2CppType{TSelf}"/>.
21+
/// For example, it can differ when the object is boxed and the type argument is an interface.
22+
/// However, the implementation of this method simply calls the method provided by <see cref="IIl2CppType{TSelf}"/> for the unboxed type.
23+
/// </remarks>
24+
/// <param name="span">The span to write the value to.</param>
25+
void WriteToSpan(Span<byte> span);
26+
}

Il2CppInterop.Runtime/InteropTypes/Il2CppTypeHelper.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,18 +138,26 @@ public static void WritePointer(nint pointer, Span<byte> span)
138138

139139
public static unsafe IntPtr Box<T>(this T? value) where T : IIl2CppType<T>
140140
{
141-
if (value is null)
141+
if (typeof(T).IsValueType)
142+
{
143+
byte* data = stackalloc byte[T.Size];
144+
WriteToPointer(value, data);
145+
IntPtr boxedPtr = IL2CPP.il2cpp_value_box(value!.ObjectClass, (IntPtr)data);
146+
return boxedPtr;
147+
}
148+
else if (value is null)
142149
{
143150
return IntPtr.Zero;
144151
}
145152
else if (value is Object @object)
146153
{
147154
return @object.Pointer;
148155
}
149-
else if (value.GetType().IsValueType)
156+
else if (value is IIl2CppValueType valueType)
150157
{
151-
byte* data = stackalloc byte[T.Size];
152-
WriteToPointer(value, data);
158+
int size = valueType.Size;
159+
byte* data = stackalloc byte[size];
160+
valueType.WriteToSpan(new Span<byte>(data, size));
153161
IntPtr boxedPtr = IL2CPP.il2cpp_value_box(value.ObjectClass, (IntPtr)data);
154162
return boxedPtr;
155163
}

0 commit comments

Comments
 (0)