From 5355d46c2d03bc97cbb716dc02a135cd0934c4f9 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 14 Sep 2024 09:06:22 -0700 Subject: [PATCH 1/3] Improve Generics Unstripping Improve Ldtoken handling during unstripping Formatting --- .../Passes/Pass80UnstripMethods.cs | 5 +- .../Utils/UnstripTranslator.cs | 88 ++++++++----------- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs index 3fce73fb..a5e304b1 100644 --- a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs +++ b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs @@ -181,9 +181,12 @@ private static PropertyDefinition GetOrCreateProperty(MethodDefinition unityMeth internal static TypeSignature? ResolveTypeInNewAssembliesRaw(RewriteGlobalContext context, TypeSignature? unityType, RuntimeAssemblyReferences imports) { - if (unityType is null or GenericParameterSignature) + if (unityType is null) return null; + if (unityType is GenericParameterSignature genericParameterSignature) + return new GenericParameterSignature(imports.Module, genericParameterSignature.ParameterType, genericParameterSignature.Index); + if (unityType is ByReferenceTypeSignature) { var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); diff --git a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs index d0b6ca98..cb9234bb 100644 --- a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs +++ b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs @@ -5,7 +5,6 @@ using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Cil; using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; using Il2CppInterop.Generator.Passes; namespace Il2CppInterop.Generator.Utils; @@ -160,7 +159,7 @@ public static bool TranslateMethod(MethodDefinition original, MethodDefinition t var methodDeclarer = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArg.DeclaringType?.ToTypeSignature(), imports); if (methodDeclarer == null) - return false; // todo: generic methods + return false; var newReturnType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArg.Signature?.ReturnType, imports); @@ -180,36 +179,40 @@ public static bool TranslateMethod(MethodDefinition original, MethodDefinition t newMethodSignature.ParameterTypes.Add(newParamType); } - var newMethod = new MemberReference(methodDeclarer.ToTypeDefOrRef(), methodArg.Name, newMethodSignature); + var memberReference = new MemberReference(methodDeclarer.ToTypeDefOrRef(), methodArg.Name, newMethodSignature); - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, imports.Module.DefaultImporter.ImportMethod(newMethod)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineType) - { - var targetType = ((ITypeDefOrRef)bodyInstruction.Operand).ToTypeSignature(); - if (targetType is GenericParameterSignature genericParam) + IMethodDescriptor newMethod; + if (methodArg is MethodSpecification genericMethod) { - if (genericParam.ParameterType is GenericParameterType.Type) + if (genericMethod.Signature is null) + return false; + + TypeSignature[] typeArguments = new TypeSignature[genericMethod.Signature.TypeArguments.Count]; + for (var i = 0; i < genericMethod.Signature.TypeArguments.Count; i++) { - var newTypeOwner = - Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, original.DeclaringType?.ToTypeSignature(), imports)?.Resolve(); - if (newTypeOwner == null) + var newTypeArgument = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, genericMethod.Signature.TypeArguments[i], imports); + if (newTypeArgument == null) return false; - targetType = newTypeOwner.GenericParameters.Single(it => it.Name == targetType.Name).ToTypeSignature(); - } - else - { - targetType = target.GenericParameters.Single(it => it.Name == targetType.Name).ToTypeSignature(); + + typeArguments[i] = newTypeArgument; } + + newMethod = memberReference.MakeGenericInstanceMethod(typeArguments); } else { - targetType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, targetType, imports); - if (targetType == null) - return false; + newMethod = memberReference; } + var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, imports.Module.DefaultImporter.ImportMethod(newMethod)); + instructionMap.Add(bodyInstruction, newInstruction); + } + else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineType) + { + var targetType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, ((ITypeDefOrRef)bodyInstruction.Operand).ToTypeSignature(), imports); + if (targetType == null) + return false; + if (bodyInstruction.OpCode == OpCodes.Castclass && !targetType.IsValueType) { var newInstruction = targetBuilder.Add(OpCodes.Call, @@ -256,36 +259,23 @@ public static bool TranslateMethod(MethodDefinition original, MethodDefinition t } else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineTok) { - var targetTok = (bodyInstruction.Operand as ITypeDefOrRef)?.ToTypeSignature(); - if (targetTok == null) - return false; - if (targetTok is GenericParameterSignature genericParam) + switch (bodyInstruction.Operand) { - if (genericParam.ParameterType is GenericParameterType.Type) - { - var newTypeOwner = - Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, original.DeclaringType?.ToTypeSignature(), imports)?.Resolve(); - if (newTypeOwner == null) - return false; - var name = original.DeclaringType!.GenericParameters[genericParam.Index].Name; - targetTok = newTypeOwner.GenericParameters.Single(it => it.Name == name).ToTypeSignature(); - } - else - { - var name = original.GenericParameters[genericParam.Index].Name; - targetTok = target.GenericParameters.Single(it => it.Name == name).ToTypeSignature(); - } - } - else - { - targetTok = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, targetTok, imports); - if (targetTok == null) + case ITypeDefOrRef typeDefOrRef: + { + var targetTok = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, typeDefOrRef.ToTypeSignature(), imports); + if (targetTok == null) + return false; + + var newInstruction = targetBuilder.Add(OpCodes.Call, + imports.Module.DefaultImporter.ImportMethod(imports.Il2CppSystemRuntimeTypeHandleGetRuntimeTypeHandle.Value.MakeGenericInstanceMethod(targetTok))); + instructionMap.Add(bodyInstruction, newInstruction); + } + break; + default: + // Ldtoken is also used for members, which is not implemented. return false; } - - var newInstruction = targetBuilder.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.Il2CppSystemRuntimeTypeHandleGetRuntimeTypeHandle.Value.MakeGenericInstanceMethod(targetTok))); - instructionMap.Add(bodyInstruction, newInstruction); } else if (bodyInstruction.OpCode.OperandType is CilOperandType.InlineSwitch && bodyInstruction.Operand is IReadOnlyList labels) { From 1872123816c12a97779112778c08dc85748197b5 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:35:05 -0700 Subject: [PATCH 2/3] Add assertion --- Il2CppInterop.Generator/Utils/UnstripTranslator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs index cb9234bb..fe1a5261 100644 --- a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs +++ b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using AsmResolver; using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; @@ -259,6 +260,7 @@ public static bool TranslateMethod(MethodDefinition original, MethodDefinition t } else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineTok) { + Debug.Assert(bodyInstruction.OpCode.Code is CilCode.Ldtoken); switch (bodyInstruction.Operand) { case ITypeDefOrRef typeDefOrRef: From 39505475d60d651aa06f02cdd5ad5203f974bb93 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Sat, 14 Sep 2024 23:32:56 -0700 Subject: [PATCH 3/3] Handle unboxing for generic parameters --- Il2CppInterop.Generator/Utils/UnstripTranslator.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs index fe1a5261..ee9ce42c 100644 --- a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs +++ b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs @@ -214,8 +214,12 @@ public static bool TranslateMethod(MethodDefinition original, MethodDefinition t if (targetType == null) return false; - if (bodyInstruction.OpCode == OpCodes.Castclass && !targetType.IsValueType) + if ((bodyInstruction.OpCode == OpCodes.Castclass && !targetType.IsValueType) || + (bodyInstruction.OpCode == OpCodes.Unbox_Any && targetType is GenericParameterSignature)) { + // Compilers use unbox.any for casting to generic parameter types. + // Castclass is only used for reference types. + // Both can be translated to Il2CppObjectBase.Cast(). var newInstruction = targetBuilder.Add(OpCodes.Call, imports.Module.DefaultImporter.ImportMethod(imports.Il2CppObjectBase_Cast.Value.MakeGenericInstanceMethod(targetType))); instructionMap.Add(bodyInstruction, newInstruction);