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..ee9ce42c 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; @@ -5,7 +6,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 +160,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,38 +180,46 @@ 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; } - if (bodyInstruction.OpCode == OpCodes.Castclass && !targetType.IsValueType) + 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) || + (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); @@ -256,36 +264,24 @@ 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) + Debug.Assert(bodyInstruction.OpCode.Code is CilCode.Ldtoken); + 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) {