Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to struct interop for DOTS #70

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5b5cefe
Basic support of struct injection
limoka Oct 28, 2022
3be8a27
Merge branch 'BepInEx:master' into master
limoka Nov 2, 2022
04ebe53
Fix methods with pointers generated wrong in interop assemblies
limoka Nov 2, 2022
a73c475
Don't generate default constructor for structs
limoka Nov 5, 2022
0e785af
Merge branch 'BepInEx:master' into master
limoka Nov 11, 2022
bdb3538
Merge branch 'BepInEx:master' into master
limoka Dec 26, 2022
010800e
Allow generic structs to be blittable
limoka Jan 21, 2023
82f7e23
Prevent duplicate IsUnmanagedAttribute classes from being generated
limoka Jan 21, 2023
d795fd9
Fix formatting
limoka Jan 22, 2023
715fbd8
Cleanup
limoka Jan 22, 2023
277efc6
Determine type usage. Add GenericBlittable struct type
limoka Jan 23, 2023
c767a78
Generate Boxed variations for generic blittable structs
limoka Feb 2, 2023
2c4a31f
Append _Unboxed for blittable types instead of non blittable
limoka Feb 14, 2023
fb8ac21
Fix incorrect generic parameter field generation
limoka May 22, 2023
9c4692e
Address comments
limoka May 22, 2023
c00299e
Merge remote-tracking branch 'origin/master' into dots-improvements
limoka May 23, 2023
3e44ef8
Fix method generic parameter constraints
limoka May 23, 2023
636648e
Relax generic constraints which are not used in any fields
limoka May 23, 2023
4232b6a
Force Nullable struct to be non blittable
limoka May 23, 2023
8b03a8d
Remove default constructor constraints
limoka May 23, 2023
682cd0b
Check nested types for generic parameter relaxation
limoka May 23, 2023
3321c8d
Fix generic constraint violations in nested types
limoka May 25, 2023
02c1637
Perform generic constraint checks to ensure no type load exceptions
limoka May 30, 2023
8963b77
Check for field generic parameter usage
limoka May 31, 2023
debe584
Split generic parameter code into pass
limoka May 31, 2023
fa78b1c
Adjust generic parameter constraint logic
limoka May 31, 2023
4ebb59c
Set Strict generic parameter constraint when found no non blittable u…
limoka Jun 1, 2023
1f16eb8
Fix formatting
limoka Jun 1, 2023
8587912
Ensure structs are not generated as blittable only when non blittable…
limoka Jun 3, 2023
ae8a1bf
Generate nested types for boxed type variants
limoka Jun 3, 2023
802eba3
Split compute specifics pass in two to prevent use cycles
limoka Jun 3, 2023
48a7cc5
Prevent relaxed generic parameters from affecting blittablity.
limoka Jun 4, 2023
de2c125
Revert forcing Nullable struct to be non blittable
limoka Jun 5, 2023
35d2507
Fix invalid constructor being generated in unboxed struct variants
limoka Jun 9, 2023
9963313
Generate boolean fields in generic structs as a custom struct.
limoka Jun 9, 2023
f38c606
Allow fields to be ignored
limoka Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 64 additions & 17 deletions Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public AssemblyRewriteContext(RewriteGlobalContext globalContext, AssemblyDefini
mod => new RuntimeAssemblyReferences(mod, globalContext));
}

public IEnumerable<TypeRewriteContext> Types => myOldTypeMap.Values;
public IEnumerable<TypeRewriteContext> Types => myNewTypeMap.Values;

public TypeRewriteContext GetContextForOriginalType(TypeDefinition type)
{
Expand All @@ -51,10 +51,12 @@ public TypeRewriteContext GetContextForNewType(TypeDefinition type)

public void RegisterTypeRewrite(TypeRewriteContext context)
{
if (context.OriginalType != null)
var exists = context.OriginalType != null && myOldTypeMap.ContainsKey(context.OriginalType);
if (context.OriginalType != null && !exists)
myOldTypeMap[context.OriginalType] = context;
myNewTypeMap[context.NewType] = context;
myNameTypeMap[(context.OriginalType ?? context.NewType).FullName] = context;
if (!exists)
myNameTypeMap[(context.OriginalType ?? context.NewType).FullName] = context;
}
limoka marked this conversation as resolved.
Show resolved Hide resolved

public MethodReference RewriteMethodRef(MethodReference methodRef)
Expand All @@ -63,7 +65,7 @@ public MethodReference RewriteMethodRef(MethodReference methodRef)
return newType.GetMethodByOldMethod(methodRef.Resolve()).NewMethod;
}

public TypeReference RewriteTypeRef(TypeReference? typeRef)
public TypeReference RewriteTypeRef(TypeReference? typeRef, bool typeIsBoxed)
{
if (typeRef == null) return Imports.Il2CppObjectBase;

Expand All @@ -78,7 +80,7 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)
if (elementType.FullName == "System.String")
return Imports.Il2CppStringArray;

var convertedElementType = RewriteTypeRef(elementType);
var convertedElementType = RewriteTypeRef(elementType, typeIsBoxed);
if (elementType.IsGenericParameter)
return new GenericInstanceType(Imports.Il2CppArrayBase) { GenericArguments = { convertedElementType } };

Expand All @@ -92,24 +94,36 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)
{
var genericParameterDeclaringType = genericParameter.DeclaringType;
if (genericParameterDeclaringType != null)
return RewriteTypeRef(genericParameterDeclaringType).GenericParameters[genericParameter.Position];
return RewriteTypeRef(genericParameterDeclaringType, typeIsBoxed).GenericParameters[genericParameter.Position];

return RewriteMethodRef(genericParameter.DeclaringMethod).GenericParameters[genericParameter.Position];
}

if (typeRef is ByReferenceType byRef)
return new ByReferenceType(RewriteTypeRef(byRef.ElementType));
return new ByReferenceType(RewriteTypeRef(byRef.ElementType, typeIsBoxed));

if (typeRef is PointerType pointerType)
return new PointerType(RewriteTypeRef(pointerType.ElementType));
return new PointerType(RewriteTypeRef(pointerType.ElementType, typeIsBoxed));

if (typeRef is GenericInstanceType genericInstance)
{
var newRef = new GenericInstanceType(RewriteTypeRef(genericInstance.ElementType));
foreach (var originalParameter in genericInstance.GenericArguments)
newRef.GenericArguments.Add(RewriteTypeRef(originalParameter));

return newRef;
var genericTypeContext = GetTypeContext(genericInstance);
if (genericTypeContext.ComputedTypeSpecifics == TypeRewriteContext.TypeSpecifics.GenericBlittableStruct && !IsUnmanaged(typeRef, typeIsBoxed))
{
var newRef = new GenericInstanceType(sourceModule.ImportReference(genericTypeContext.BoxedTypeContext.NewType));
foreach (var originalParameter in genericInstance.GenericArguments)
newRef.GenericArguments.Add(RewriteTypeRef(originalParameter, typeIsBoxed));

return newRef;
}
else
{
var newRef = new GenericInstanceType(RewriteTypeRef(genericInstance.ElementType, typeIsBoxed));
foreach (var originalParameter in genericInstance.GenericArguments)
newRef.GenericArguments.Add(RewriteTypeRef(originalParameter, typeIsBoxed));

return newRef;
}
}

if (typeRef.IsPrimitive || typeRef.FullName == "System.TypedReference")
Expand All @@ -129,11 +143,44 @@ public TypeReference RewriteTypeRef(TypeReference? typeRef)
return sourceModule.ImportReference(GlobalContext.GetAssemblyByName("mscorlib")
.GetTypeByName("System.Attribute").NewType);

var originalTypeDef = typeRef.Resolve();
var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(originalTypeDef.Module.Assembly);
var target = targetAssembly.GetContextForOriginalType(originalTypeDef).NewType;
var target = GetTypeContext(typeRef);

if (typeIsBoxed && target.BoxedTypeContext != null)
{
target = target.BoxedTypeContext;
}

return sourceModule.ImportReference(target.NewType);
}

private TypeRewriteContext GetTypeContext(TypeReference typeRef)
{
var typeDef = typeRef.Resolve();
var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(typeDef.Module.Assembly);
var typeContext = targetAssembly.GetContextForOriginalType(typeDef);

return typeContext;
}

private bool IsUnmanaged(TypeReference originalType, bool typeIsBoxed)
{
if (originalType is GenericParameter genericParameter)
{
GenericParameter newGenericParameter = (GenericParameter)RewriteTypeRef(genericParameter, typeIsBoxed);
return newGenericParameter.CustomAttributes.Any(attribute => attribute.AttributeType.Name.Equals("IsUnmanagedAttribute"));
}

if (originalType is GenericInstanceType genericInstanceType)
{
foreach (TypeReference genericArgument in genericInstanceType.GenericArguments)
{
if (!IsUnmanaged(genericArgument, typeIsBoxed))
return false;
}
}

return sourceModule.ImportReference(target);
var paramTypeContext = GetTypeContext(originalType);
return paramTypeContext.ComputedTypeSpecifics.IsBlittable();
}

public TypeRewriteContext GetTypeByName(string name)
Expand Down
2 changes: 1 addition & 1 deletion Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private string UnmangleFieldNameBase(FieldDefinition field, GeneratorOptions opt
var accessModString = MethodAccessTypeLabels[(int)(field.Attributes & FieldAttributes.FieldAccessMask)];
var staticString = field.IsStatic ? "_Static" : "";
return "field_" + accessModString + staticString + "_" +
DeclaringType.AssemblyContext.RewriteTypeRef(field.FieldType).GetUnmangledName();
DeclaringType.AssemblyContext.RewriteTypeRef(field.FieldType, false).GetUnmangledName();
}

private string UnmangleFieldName(FieldDefinition field, GeneratorOptions options,
Expand Down
8 changes: 4 additions & 4 deletions Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public void CtorPhase2()
UnmangledNameWithSignature = UnmangleMethodNameWithSignature();

NewMethod.Name = UnmangledName;
NewMethod.ReturnType = DeclaringType.AssemblyContext.RewriteTypeRef(OriginalMethod.ReturnType);
NewMethod.ReturnType = DeclaringType.AssemblyContext.RewriteTypeRef(OriginalMethod.ReturnType, DeclaringType.isBoxedTypeVariant);

var nonGenericMethodInfoPointerField = new FieldDefinition(
"NativeMethodInfoPtr_" + UnmangledNameWithSignature,
Expand Down Expand Up @@ -140,7 +140,7 @@ public void CtorPhase2()
oldConstraint.ConstraintType.Resolve()?.IsInterface == true) continue;

newParameter.Constraints.Add(new GenericParameterConstraint(
DeclaringType.AssemblyContext.RewriteTypeRef(oldConstraint.ConstraintType)));
DeclaringType.AssemblyContext.RewriteTypeRef(oldConstraint.ConstraintType, DeclaringType.isBoxedTypeVariant)));
}
}

Expand Down Expand Up @@ -220,12 +220,12 @@ private string ProduceMethodSignatureBase()
builder.Append(str);

builder.Append('_');
builder.Append(DeclaringType.AssemblyContext.RewriteTypeRef(method.ReturnType).GetUnmangledName());
builder.Append(DeclaringType.AssemblyContext.RewriteTypeRef(method.ReturnType, DeclaringType.isBoxedTypeVariant).GetUnmangledName());

foreach (var param in method.Parameters)
{
builder.Append('_');
builder.Append(DeclaringType.AssemblyContext.RewriteTypeRef(param.ParameterType).GetUnmangledName());
builder.Append(DeclaringType.AssemblyContext.RewriteTypeRef(param.ParameterType, DeclaringType.isBoxedTypeVariant).GetUnmangledName());
}

var address = Rva;
Expand Down
9 changes: 6 additions & 3 deletions Il2CppInterop.Generator/Contexts/TypeRewriteContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Il2CppInterop.Generator.Passes;
using Il2CppInterop.Generator.Utils;
using Mono.Cecil;

Expand All @@ -13,6 +14,7 @@ public enum TypeSpecifics
Computing,
ReferenceType,
BlittableStruct,
GenericBlittableStruct,
NonBlittableStruct
}

Expand All @@ -22,6 +24,8 @@ public enum TypeSpecifics
private readonly Dictionary<MethodDefinition, MethodRewriteContext> myMethodContexts = new();
private readonly Dictionary<string, MethodRewriteContext> myMethodContextsByName = new();
public readonly TypeDefinition NewType;
public TypeRewriteContext BoxedTypeContext;
public bool isBoxedTypeVariant;

public readonly bool OriginalNameWasObfuscated;
public readonly TypeDefinition OriginalType;
Expand All @@ -37,7 +41,8 @@ public TypeRewriteContext(AssemblyRewriteContext assemblyContext, TypeDefinition

if (OriginalType == null) return;

OriginalNameWasObfuscated = OriginalType.Name != NewType.Name;
OriginalNameWasObfuscated = OriginalType.Name != NewType.Name &&
Pass12CreateGenericNonBlittableTypes.GetNewName(originalType.Name) != NewType.Name;
if (OriginalNameWasObfuscated)
NewType.CustomAttributes.Add(new CustomAttribute(assemblyContext.Imports.ObfuscatedNameAttributector.Value)
{
Expand All @@ -49,8 +54,6 @@ public TypeRewriteContext(AssemblyRewriteContext assemblyContext, TypeDefinition
ComputedTypeSpecifics = TypeSpecifics.ReferenceType;
else if (OriginalType.IsEnum)
ComputedTypeSpecifics = TypeSpecifics.BlittableStruct;
else if (OriginalType.HasGenericParameters)
ComputedTypeSpecifics = TypeSpecifics.NonBlittableStruct; // not reference type, covered by first if
}

public FieldReference ClassPointerFieldRef { get; private set; }
Expand Down
7 changes: 7 additions & 0 deletions Il2CppInterop.Generator/Extensions/EnumEx.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Il2CppInterop.Generator.Contexts;
using Mono.Cecil;

namespace Il2CppInterop.Generator.Extensions;
Expand All @@ -16,4 +17,10 @@ public static GenericParameterAttributes StripValueTypeConstraint(
return parameterAttributes & ~(GenericParameterAttributes.NotNullableValueTypeConstraint |
GenericParameterAttributes.VarianceMask);
}

public static bool IsBlittable(this TypeRewriteContext.TypeSpecifics typeSpecifics)
{
return typeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct ||
typeSpecifics == TypeRewriteContext.TypeSpecifics.GenericBlittableStruct;
}
}
14 changes: 14 additions & 0 deletions Il2CppInterop.Generator/Extensions/ILGeneratorEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ public static void EmitObjectToPointer(this ILProcessor body, TypeReference orig
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Call, imports.IL2CPP_ManagedStringToIl2Cpp.Value);
}
else if (originalType.IsPointer)
{
body.Emit(OpCodes.Ldarg, argumentIndex);
body.Emit(OpCodes.Call, new MethodReference("op_Explicit", imports.Module.IntPtr(), imports.Module.IntPtr())
{ Parameters = { new ParameterDefinition(imports.Module.ImportReference(typeof(void*))) } });
}
else
{
body.Emit(OpCodes.Ldarg, argumentIndex);
Expand Down Expand Up @@ -352,6 +358,13 @@ public static void EmitPointerToObject(this ILProcessor body, TypeReference orig
{ HasThis = false, Parameters = { new ParameterDefinition(imports.Module.IntPtr()) } };
body.Emit(OpCodes.Call, methodRef);
}
else if (originalReturnType.IsPointer)
{
body.Append(loadPointer);
body.Emit(OpCodes.Call,
new MethodReference("op_Explicit", imports.Module.ImportReference(typeof(void*)), imports.Module.IntPtr())
{ Parameters = { new ParameterDefinition(imports.Module.IntPtr()) } });
}
else
{
var createRealObject = body.Create(OpCodes.Newobj,
Expand Down Expand Up @@ -383,6 +396,7 @@ private static void EmitPointerToObjectGeneric(ILProcessor body, TypeReference o

body.Emit(extraDerefForNonValueTypes ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
body.Emit(unboxValueType ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);

body.Emit(OpCodes.Call,
imports.Module.ImportReference(new GenericInstanceMethod(imports.IL2CPP_PointerToValueGeneric.Value)
{ GenericArguments = { newReturnType } }));
Expand Down
Loading