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 all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d888818
Basic support of struct injection
limoka Oct 28, 2022
db24493
Fix methods with pointers generated wrong in interop assemblies
limoka Nov 2, 2022
04f81c5
Don't generate default constructor for structs
limoka Nov 5, 2022
efb6bbe
Allow generic structs to be blittable
limoka Jan 21, 2023
1816f85
Prevent duplicate IsUnmanagedAttribute classes from being generated
limoka Jan 21, 2023
a358207
Fix formatting
limoka Jan 22, 2023
5663946
Cleanup
limoka Jan 22, 2023
206a033
Determine type usage. Add GenericBlittable struct type
limoka Jan 23, 2023
421463b
Generate Boxed variations for generic blittable structs
limoka Feb 2, 2023
c791c55
Append _Unboxed for blittable types instead of non blittable
limoka Feb 14, 2023
f9d45e4
Fix incorrect generic parameter field generation
limoka May 22, 2023
6edae0f
Address comments
limoka May 22, 2023
e64d831
Fix method generic parameter constraints
limoka May 23, 2023
a2ad716
Relax generic constraints which are not used in any fields
limoka May 23, 2023
ef48544
Force Nullable struct to be non blittable
limoka May 23, 2023
1958135
Remove default constructor constraints
limoka May 23, 2023
dbf9b5c
Check nested types for generic parameter relaxation
limoka May 23, 2023
fbce4e6
Fix generic constraint violations in nested types
limoka May 25, 2023
e4ac133
Perform generic constraint checks to ensure no type load exceptions
limoka May 30, 2023
003d358
Check for field generic parameter usage
limoka May 31, 2023
77c056f
Split generic parameter code into pass
limoka May 31, 2023
0109f75
Adjust generic parameter constraint logic
limoka May 31, 2023
a39d55f
Set Strict generic parameter constraint when found no non blittable u…
limoka Jun 1, 2023
1a13f17
Fix formatting
limoka Jun 1, 2023
c1b7a61
Ensure structs are not generated as blittable only when non blittable…
limoka Jun 3, 2023
5a9c1bb
Generate nested types for boxed type variants
limoka Jun 3, 2023
b9af2b1
Split compute specifics pass in two to prevent use cycles
limoka Jun 3, 2023
84bf008
Prevent relaxed generic parameters from affecting blittablity.
limoka Jun 4, 2023
1ecdb90
Revert forcing Nullable struct to be non blittable
limoka Jun 5, 2023
2e07ccb
Fix invalid constructor being generated in unboxed struct variants
limoka Jun 9, 2023
2536c32
Generate boolean fields in generic structs as a custom struct.
limoka Jun 9, 2023
68dec04
Allow fields to be ignored
limoka Jun 10, 2023
8558a20
Port generic struct generation changes to use AsmResolver
limoka Jan 21, 2025
6663045
Fix generation inconsistencies with previous version
limoka Jan 23, 2025
8cf85ad
fix formatting issues
limoka Jan 23, 2025
b13e2a6
tidy up code
limoka Jan 23, 2025
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
137 changes: 118 additions & 19 deletions Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Diagnostics;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Metadata.Tables;
using Il2CppInterop.Generator.Extensions;
using Il2CppInterop.Generator.Utils;

Expand All @@ -14,6 +16,7 @@
public readonly RuntimeAssemblyReferences Imports;
private readonly Dictionary<string, TypeRewriteContext> myNameTypeMap = new();
private readonly Dictionary<TypeDefinition, TypeRewriteContext> myNewTypeMap = new();
private TypeDefinition? isUnmanagedAttributeType;

private readonly Dictionary<TypeDefinition, TypeRewriteContext> myOldTypeMap = new();
public readonly AssemblyDefinition NewAssembly;
Expand All @@ -31,7 +34,8 @@
mod => new RuntimeAssemblyReferences(mod, globalContext));
}

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

public TypeRewriteContext GetContextForOriginalType(TypeDefinition type)
{
Expand All @@ -48,6 +52,11 @@
return myNewTypeMap[type];
}

public void RegisterTypeContext(TypeRewriteContext context)
{
myNewTypeMap.Add(context.NewType, context);
}

public void RegisterTypeRewrite(TypeRewriteContext context)
{
if (context.OriginalType != null)
Expand All @@ -63,12 +72,26 @@
return NewAssembly.ManifestModule!.DefaultImporter.ImportMethod(newMethod);
}

public ITypeDefOrRef RewriteTypeRef(ITypeDescriptor typeRef)
/// <summary>
/// Rewrite passes type
/// </summary>
/// <param name="typeRef">type</param>
/// <param name="context">generic context. Ensure you pass NEW types here</param>
/// <param name="typeIsBoxed">are we rewriting boxed variant</param>
/// <returns>new type</returns>
public ITypeDefOrRef RewriteTypeRef(ITypeDescriptor typeRef, GenericParameterContext context = default, bool typeIsBoxed = false)
{
return RewriteTypeRef(typeRef?.ToTypeSignature()).ToTypeDefOrRef();
return RewriteTypeRef(typeRef?.ToTypeSignature(), context, typeIsBoxed).ToTypeDefOrRef();
}

public TypeSignature RewriteTypeRef(TypeSignature? typeRef)
/// <summary>
/// Rewrite passes type
/// </summary>
/// <param name="typeRef">type</param>
/// <param name="context">generic context. Ensure you pass NEW types here</param>
/// <param name="typeIsBoxed">are we rewriting boxed variant</param>
/// <returns>new type</returns>
public TypeSignature RewriteTypeRef(TypeSignature? typeRef, GenericParameterContext context = default, bool typeIsBoxed = false)
{
if (typeRef == null)
return Imports.Il2CppObjectBase;
Expand All @@ -84,7 +107,7 @@
if (elementType.FullName == "System.String")
return Imports.Il2CppStringArray;

var convertedElementType = RewriteTypeRef(elementType);
var convertedElementType = RewriteTypeRef(elementType, context, typeIsBoxed);
if (elementType is GenericParameterSignature)
return new GenericInstanceTypeSignature(Imports.Il2CppArrayBase.ToTypeDefOrRef(), false, convertedElementType);

Expand All @@ -94,24 +117,35 @@
}

if (typeRef is GenericParameterSignature genericParameter)
{
return new GenericParameterSignature(sourceModule, genericParameter.ParameterType, genericParameter.Index);
}

if (typeRef is ByReferenceTypeSignature byRef)
return new ByReferenceTypeSignature(RewriteTypeRef(byRef.BaseType));
return new ByReferenceTypeSignature(RewriteTypeRef(byRef.BaseType, context, typeIsBoxed));

if (typeRef is PointerTypeSignature pointerType)
return new PointerTypeSignature(RewriteTypeRef(pointerType.BaseType));
return new PointerTypeSignature(RewriteTypeRef(pointerType.BaseType, context, typeIsBoxed));

if (typeRef is GenericInstanceTypeSignature genericInstance)
{
var genericType = RewriteTypeRef(genericInstance.GenericType.ToTypeSignature()).ToTypeDefOrRef();
var newRef = new GenericInstanceTypeSignature(genericType, genericType.IsValueType);
foreach (var originalParameter in genericInstance.TypeArguments)
newRef.TypeArguments.Add(RewriteTypeRef(originalParameter));

return newRef;
var genericTypeContext = GetTypeContext(genericInstance);
if (genericTypeContext.ComputedTypeSpecifics == TypeRewriteContext.TypeSpecifics.GenericBlittableStruct && !IsUnmanaged(typeRef, context))
{
var type = sourceModule.DefaultImporter.ImportType(genericTypeContext.BoxedTypeContext.NewType);

Check warning on line 133 in Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
var newRef = new GenericInstanceTypeSignature(type, type.IsValueType);
foreach (var originalParameter in genericInstance.TypeArguments)
newRef.TypeArguments.Add(RewriteTypeRef(originalParameter, context, typeIsBoxed));

return newRef;
}
else
{
var genericType = RewriteTypeRef(genericInstance.GenericType.ToTypeSignature(), context, typeIsBoxed).ToTypeDefOrRef();
var newRef = new GenericInstanceTypeSignature(genericType, genericType.IsValueType);
foreach (var originalParameter in genericInstance.TypeArguments)
newRef.TypeArguments.Add(RewriteTypeRef(originalParameter, context, typeIsBoxed));

return newRef;
}
}

if (typeRef.IsPrimitive() || typeRef.FullName == "System.TypedReference")
Expand All @@ -131,11 +165,76 @@
return sourceModule.DefaultImporter.ImportType(GlobalContext.GetAssemblyByName("mscorlib")
.GetTypeByName("System.Attribute").NewType).ToTypeSignature();

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.DefaultImporter.ImportType(target.NewType).ToTypeSignature();
}

private TypeRewriteContext GetTypeContext(TypeSignature typeRef)
{
return GlobalContext.GetNewTypeForOriginal(typeRef.Resolve()!);
}

private bool IsUnmanaged(TypeSignature originalType, GenericParameterContext context)
{
if (originalType is GenericParameterSignature parameterSignature)
{
var genericParameter = context.GetGenericParameter(parameterSignature)!;
return genericParameter.IsUnmanaged();
}

if (originalType is GenericInstanceTypeSignature genericInstanceType)
{
foreach (TypeSignature genericArgument in genericInstanceType.TypeArguments)
{
if (!IsUnmanaged(genericArgument, context))
return false;
}
}

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

public TypeDefinition GetOrInjectIsUnmanagedAttribute()
{
if (isUnmanagedAttributeType != null)
return isUnmanagedAttributeType;

var importer = NewAssembly.ManifestModule!.DefaultImporter;

isUnmanagedAttributeType = new TypeDefinition(
"System.Runtime.CompilerServices",
"IsUnmanagedAttribute",
TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.Public | TypeAttributes.Sealed,
importer.ImportType(typeof(Attribute)));

NewAssembly.ManifestModule!.TopLevelTypes.Add(isUnmanagedAttributeType);

var attributeCctr = new MethodDefinition(
".ctor",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RuntimeSpecialName | MethodAttributes.SpecialName,
MethodSignature.CreateInstance(NewAssembly.ManifestModule.Void()));
attributeCctr.CilMethodBody = new CilMethodBody(attributeCctr);

isUnmanagedAttributeType.Methods.Add(attributeCctr);
var ilProcessor = attributeCctr.CilMethodBody!.Instructions;
ilProcessor.Add(OpCodes.Ldarg_0);

var method = new MemberReference(
isUnmanagedAttributeType.BaseType!,
".ctor",
MethodSignature.CreateInstance(NewAssembly.ManifestModule.Void()));

ilProcessor.Add(OpCodes.Call, importer.ImportMethod(method));
ilProcessor.Add(OpCodes.Ret);

return sourceModule.DefaultImporter.ImportType(target).ToTypeSignature();
return isUnmanagedAttributeType;
}

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 @@ -51,7 +51,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.Signature!.FieldType).GetUnmangledName(field.DeclaringType);
DeclaringType.AssemblyContext.RewriteTypeRef(field.Signature!.FieldType, field.DeclaringType!.GetGenericParameterContext()).GetUnmangledName(field.DeclaringType);
}

private string UnmangleFieldName(FieldDefinition field, GeneratorOptions options,
Expand Down
90 changes: 68 additions & 22 deletions Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Text;
using AsmResolver;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Collections;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Metadata.Tables;
using Il2CppInterop.Common.XrefScans;
Expand Down Expand Up @@ -74,15 +75,24 @@ public MethodRewriteContext(TypeRewriteContext declaringType, MethodDefinition o

foreach (var oldParameter in genericParams)
{
newMethod.GenericParameters.Add(new GenericParameter(
oldParameter.Name.MakeValidInSource(),
oldParameter.Attributes.StripValueTypeConstraint()));
var genericParameter = new GenericParameter(oldParameter.Name.MakeValidInSource());
if (ShouldParameterBeBlittable(originalMethod, oldParameter.ToTypeSignature()))
{
genericParameter.Attributes = oldParameter.Attributes;
genericParameter.MakeUnmanaged(DeclaringType.AssemblyContext);
}
else
{
genericParameter.Attributes = oldParameter.Attributes.StripValueTypeConstraint();
}

newMethod.GenericParameters.Add(genericParameter);
}
}

if (!Pass15GenerateMemberContexts.HasObfuscatedMethods && !passthroughNames &&
if (!Pass16GenerateMemberContexts.HasObfuscatedMethods && !passthroughNames &&
originalMethod.Name.IsObfuscated(declaringType.AssemblyContext.GlobalContext.Options))
Pass15GenerateMemberContexts.HasObfuscatedMethods = true;
Pass16GenerateMemberContexts.HasObfuscatedMethods = true;

FileOffset = originalMethod.ExtractOffset();
// Workaround for garbage file offsets passed by Cpp2IL
Expand All @@ -92,6 +102,38 @@ public MethodRewriteContext(TypeRewriteContext declaringType, MethodDefinition o
declaringType.AssemblyContext.GlobalContext.MethodStartAddresses.Add(FileOffset);
}

private bool ShouldParameterBeBlittable(MethodDefinition method, GenericParameterSignature genericParameter)
{
if (HasGenericParameter(method.Signature!.ReturnType, genericParameter, out GenericParameter? parameter))
{
return parameter!.IsUnmanaged();
}

foreach (Parameter methodParameter in method.Parameters)
{
if (HasGenericParameter(methodParameter.ParameterType, genericParameter, out parameter))
{
return parameter!.IsUnmanaged();
}
}

return false;
}

private bool HasGenericParameter(TypeSignature typeReference, GenericParameterSignature inputGenericParameter, out GenericParameter? typeGenericParameter)
{
typeGenericParameter = null;
if (typeReference is not GenericInstanceTypeSignature genericInstance) return false;

var index = genericInstance.TypeArguments.IndexOf(inputGenericParameter);
if (index < 0) return false;

var globalContext = DeclaringType.AssemblyContext.GlobalContext;
var returnTypeContext = globalContext.GetNewTypeForOriginal(typeReference.Resolve()!);
typeGenericParameter = returnTypeContext.NewType.GenericParameters[index];
return true;
}

public Utf8String? UnmangledName { get; private set; }
public string? UnmangledNameWithSignature { get; private set; }

Expand All @@ -104,11 +146,14 @@ public MethodRewriteContext(TypeRewriteContext declaringType, MethodDefinition o

public void CtorPhase2()
{
UnmangledName = UnmangleMethodName();
UnmangledNameWithSignature = UnmangleMethodNameWithSignature();
// Make context manually, NewMethod.DeclaringType is null for now
var genericContext = new GenericParameterContext(DeclaringType.NewType, NewMethod);

UnmangledName = UnmangleMethodName(genericContext);
UnmangledNameWithSignature = UnmangleMethodNameWithSignature(genericContext);

NewMethod.Name = UnmangledName;
NewMethod.Signature!.ReturnType = DeclaringType.AssemblyContext.RewriteTypeRef(OriginalMethod.Signature?.ReturnType);
NewMethod.Signature!.ReturnType = DeclaringType.AssemblyContext.RewriteTypeRef(OriginalMethod.Signature?.ReturnType, genericContext, DeclaringType.isBoxedTypeVariant);

var nonGenericMethodInfoPointerField = new FieldDefinition(
"NativeMethodInfoPtr_" + UnmangledNameWithSignature,
Expand Down Expand Up @@ -153,8 +198,9 @@ public void CtorPhase2()
continue;
}

newParameter.Constraints.Add(new GenericParameterConstraint(
DeclaringType.AssemblyContext.RewriteTypeRef(oldConstraint.Constraint?.ToTypeSignature()).ToTypeDefOrRef()));
var newType = DeclaringType.AssemblyContext.RewriteTypeRef(oldConstraint.Constraint?.ToTypeSignature(), default, DeclaringType.isBoxedTypeVariant)
.ToTypeDefOrRef();
newParameter.Constraints.Add(new GenericParameterConstraint(newType));
}
}

Expand Down Expand Up @@ -184,7 +230,7 @@ private MethodAttributes AdjustAttributes(MethodAttributes original, bool stripV
return original;
}

private string UnmangleMethodName()
private string UnmangleMethodName(GenericParameterContext context)
{
var method = OriginalMethod;

Expand All @@ -198,12 +244,12 @@ private string UnmangleMethodName()
return ".ctor";

if (method.Name.IsObfuscated(DeclaringType.AssemblyContext.GlobalContext.Options))
return UnmangleMethodNameWithSignature();
return UnmangleMethodNameWithSignature(context);

return method.Name.MakeValidInSource();
}

private string ProduceMethodSignatureBase()
private string ProduceMethodSignatureBase(GenericParameterContext context)
{
var method = OriginalMethod;

Expand Down Expand Up @@ -231,25 +277,25 @@ private string ProduceMethodSignatureBase()
builder.Append(str);

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

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

var address = Rva;
if (address != 0 && Pass15GenerateMemberContexts.HasObfuscatedMethods &&
!Pass16ScanMethodRefs.NonDeadMethods.Contains(address)) builder.Append("_PDM");
if (address != 0 && Pass16GenerateMemberContexts.HasObfuscatedMethods &&
!Pass17ScanMethodRefs.NonDeadMethods.Contains(address)) builder.Append("_PDM");

return builder.ToString();
}


private string UnmangleMethodNameWithSignature()
private string UnmangleMethodNameWithSignature(GenericParameterContext context)
{
var unmangleMethodNameWithSignature = ProduceMethodSignatureBase() + "_" + DeclaringType.Methods
var unmangleMethodNameWithSignature = ProduceMethodSignatureBase(context) + "_" + DeclaringType.Methods
.Where(ParameterSignatureMatchesThis).TakeWhile(it => it != this).Count();

if (DeclaringType.AssemblyContext.GlobalContext.Options.RenameMap.TryGetValue(
Expand Down Expand Up @@ -296,13 +342,13 @@ private bool ParameterSignatureMatchesThis(MethodRewriteContext otherRewriteCont
if (a[i].ParameterType.FullName != b[i].ParameterType.FullName)
return false;

if (Pass15GenerateMemberContexts.HasObfuscatedMethods)
if (Pass16GenerateMemberContexts.HasObfuscatedMethods)
{
var addressA = otherRewriteContext.Rva;
var addressB = Rva;
if (addressA != 0 && addressB != 0)
if (Pass16ScanMethodRefs.NonDeadMethods.Contains(addressA) !=
Pass16ScanMethodRefs.NonDeadMethods.Contains(addressB))
if (Pass17ScanMethodRefs.NonDeadMethods.Contains(addressA) !=
Pass17ScanMethodRefs.NonDeadMethods.Contains(addressB))
return false;
}

Expand Down
Loading
Loading