private static void EmitPointerToObjectGeneric(ILProcessor body, TypeReference originalReturnType, TypeReference newReturnType, TypeRewriteContext enclosingType, Instruction loadPointer, bool extraDerefForNonValueTypes, bool unboxValueType) { var imports = enclosingType.AssemblyContext.Imports; body.Append(loadPointer); 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.Il2CppPointerToGeneric) { GenericArguments = { newReturnType } })); }
private static void EmitObjectToPointerGeneric(ILProcessor body, TypeReference originalType, TypeReference newType, TypeRewriteContext enclosingType, int argumentIndex, bool valueTypeArgument0IsAPointer, bool allowNullable, bool unboxNonBlittableType) { var imports = enclosingType.AssemblyContext.Imports; body.Emit(OpCodes.Ldtoken, newType); body.Emit(OpCodes.Call, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == nameof(Type.GetTypeFromHandle)))); body.Emit(OpCodes.Callvirt, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == typeof(Type).GetProperty(nameof(Type.IsValueType)) !.GetMethod !.Name))); var finalNop = body.Create(OpCodes.Nop); var valueTypeNop = body.Create(OpCodes.Nop); var stringNop = body.Create(OpCodes.Nop); body.Emit(OpCodes.Brtrue, valueTypeNop); body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Box, newType); body.Emit(OpCodes.Dup); body.Emit(OpCodes.Isinst, imports.String); body.Emit(OpCodes.Brtrue_S, stringNop); body.Emit(OpCodes.Isinst, imports.Il2CppObjectBase); body.Emit(OpCodes.Call, allowNullable ? imports.Il2CppObjectBaseToPointer : imports.Il2CppObjectBaseToPointerNotNull); if (unboxNonBlittableType) { body.Emit(OpCodes.Dup); body.Emit(OpCodes.Brfalse_S, finalNop); // return null immediately body.Emit(OpCodes.Dup); body.Emit(OpCodes.Call, imports.ObjectGetClass); body.Emit(OpCodes.Call, imports.ClassIsValueType); body.Emit(OpCodes.Brfalse_S, finalNop); // return reference types immediately body.Emit(OpCodes.Call, imports.ObjectUnbox); } body.Emit(OpCodes.Br, finalNop); body.Append(stringNop); body.Emit(OpCodes.Isinst, imports.String); body.Emit(OpCodes.Call, imports.StringToNative); body.Emit(OpCodes.Br_S, finalNop); body.Append(valueTypeNop); body.Emit(OpCodes.Ldarga, argumentIndex); body.Append(finalNop); }
private static void ComputeSpecifics(TypeRewriteContext typeContext) { if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.NotComputed) { return; } typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.Computing; foreach (var originalField in typeContext.OriginalType.Fields) { if (originalField.IsStatic) { continue; } var fieldType = originalField.FieldType; if (fieldType.IsPrimitive || fieldType.IsPointer) { continue; } if (fieldType.FullName == "System.String" || fieldType.FullName == "System.Object" || fieldType.IsArray || fieldType.IsByReference || fieldType.IsGenericParameter || fieldType.IsGenericInstance) { typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.NonBlittableStruct; return; } var fieldTypeContext = typeContext.AssemblyContext.GlobalContext.GetNewTypeForOriginal(fieldType.Resolve()); ComputeSpecifics(fieldTypeContext); if (fieldTypeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct) { typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.NonBlittableStruct; return; } } typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.BlittableStruct; }
private static void GenerateStaticProxy(AssemblyRewriteContext assemblyContext, TypeRewriteContext typeContext) { var oldType = typeContext.OriginalType; var newType = typeContext.NewType; if (oldType.IsEnum) { return; } var staticCtorMethod = new MethodDefinition(".cctor", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName, assemblyContext.Imports.Void); newType.Methods.Add(staticCtorMethod); var ctorBuilder = staticCtorMethod.Body.GetILProcessor(); if (newType.IsNested) { ctorBuilder.Emit(OpCodes.Ldsfld, assemblyContext.GlobalContext.GetNewTypeForOriginal(oldType.DeclaringType).ClassPointerFieldRef); ctorBuilder.Emit(OpCodes.Ldstr, oldType.Name); ctorBuilder.Emit(OpCodes.Call, assemblyContext.Imports.GetIl2CppNestedClass); } else { ctorBuilder.Emit(OpCodes.Ldstr, oldType.Module.Name); ctorBuilder.Emit(OpCodes.Ldstr, oldType.Namespace); ctorBuilder.Emit(OpCodes.Ldstr, oldType.Name); ctorBuilder.Emit(OpCodes.Call, assemblyContext.Imports.GetIl2CppGlobalClass); } if (oldType.HasGenericParameters) { var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.Type"); var il2CppSystemTypeRef = newType.Module.ImportReference(il2CppTypeTypeRewriteContext.NewType); var il2CppTypeHandleTypeRewriteContext = assemblyContext.GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.RuntimeTypeHandle"); var il2CppSystemTypeHandleRef = newType.Module.ImportReference(il2CppTypeHandleTypeRewriteContext.NewType); ctorBuilder.Emit(OpCodes.Call, assemblyContext.Imports.GetIl2CppTypeFromClass); ctorBuilder.Emit(OpCodes.Call, new MethodReference("internal_from_handle", il2CppSystemTypeRef, il2CppSystemTypeRef) { Parameters = { new ParameterDefinition(assemblyContext.Imports.IntPtr) } }); ctorBuilder.EmitLdcI4(oldType.GenericParameters.Count); ctorBuilder.Emit(OpCodes.Newarr, il2CppSystemTypeRef); for (var i = 0; i < oldType.GenericParameters.Count; i++) { ctorBuilder.Emit(OpCodes.Dup); ctorBuilder.EmitLdcI4(i); var param = oldType.GenericParameters[i]; var storeRef = new GenericInstanceType(assemblyContext.Imports.Il2CppClassPointerStore) { GenericArguments = { param } }; var fieldRef = new FieldReference(nameof(Il2CppClassPointerStore <object> .NativeClassPtr), assemblyContext.Imports.IntPtr, storeRef); ctorBuilder.Emit(OpCodes.Ldsfld, fieldRef); ctorBuilder.Emit(OpCodes.Call, assemblyContext.Imports.GetIl2CppTypeFromClass); ctorBuilder.Emit(OpCodes.Call, new MethodReference("internal_from_handle", il2CppSystemTypeRef, il2CppSystemTypeRef) { Parameters = { new ParameterDefinition(assemblyContext.Imports.IntPtr) } }); ctorBuilder.Emit(OpCodes.Stelem_Ref); } var il2CppTypeArray = new GenericInstanceType(assemblyContext.Imports.Il2CppReferenceArray) { GenericArguments = { il2CppSystemTypeRef } }; ctorBuilder.Emit(OpCodes.Newobj, new MethodReference(".ctor", assemblyContext.Imports.Void, il2CppTypeArray) { HasThis = true, Parameters = { new ParameterDefinition(new ArrayType(assemblyContext.Imports.Il2CppReferenceArray.GenericParameters[0])) } }); ctorBuilder.Emit(OpCodes.Call, new MethodReference(nameof(Type.MakeGenericType), il2CppSystemTypeRef, il2CppSystemTypeRef) { HasThis = true, Parameters = { new ParameterDefinition(il2CppTypeArray) } }); ctorBuilder.Emit(OpCodes.Call, new MethodReference(typeof(Type).GetProperty(nameof(Type.TypeHandle)) !.GetMethod !.Name, il2CppSystemTypeHandleRef, il2CppSystemTypeRef) { HasThis = true });
private static (TypeRewriteContext?, int) FindBestMatchType(TypeRewriteContext obfType, AssemblyRewriteContext cleanAssembly, TypeRewriteContext?enclosingCleanType) { var inheritanceDepthOfOriginal = 0; var currentBase = obfType.OriginalType.BaseType; while (true) { if (currentBase == null) { break; } var currentBaseContext = obfType.AssemblyContext.GlobalContext.TryGetNewTypeForOriginal(currentBase.Resolve()); if (currentBaseContext == null || !currentBaseContext.OriginalNameWasObfuscated) { break; } inheritanceDepthOfOriginal++; currentBase = currentBaseContext.OriginalType.BaseType; } var bestPenalty = int.MinValue; TypeRewriteContext?bestMatch = null; var source = enclosingCleanType?.OriginalType.NestedTypes.Select(it => cleanAssembly.GlobalContext.GetNewTypeForOriginal(it)) ?? cleanAssembly.Types.Where(it => it.NewType.DeclaringType == null); foreach (var candidateCleanType in source) { if (obfType.OriginalType.HasMethods != candidateCleanType.OriginalType.HasMethods) { continue; } if (obfType.OriginalType.HasFields != candidateCleanType.OriginalType.HasFields) { continue; } if (obfType.OriginalType.IsEnum) { if (obfType.OriginalType.Fields.Count != candidateCleanType.OriginalType.Fields.Count) { continue; } } int currentPenalty = 0; var tryBase = candidateCleanType.OriginalType.BaseType; var actualBaseDepth = 0; while (tryBase != null) { if (tryBase?.Name == currentBase?.Name && tryBase?.Namespace == currentBase?.Namespace) { break; } tryBase = tryBase?.Resolve().BaseType; actualBaseDepth++; } if (tryBase == null && currentBase != null) { continue; } var baseDepthDifference = Math.Abs(actualBaseDepth - inheritanceDepthOfOriginal); if (baseDepthDifference > 1) { continue; // heuristic optimization } currentPenalty -= baseDepthDifference * 50; currentPenalty -= Math.Abs(candidateCleanType.OriginalType.Fields.Count - obfType.OriginalType.Fields.Count) * 5; currentPenalty -= Math.Abs(obfType.OriginalType.NestedTypes.Count - candidateCleanType.OriginalType.NestedTypes.Count) * 10; currentPenalty -= Math.Abs(obfType.OriginalType.Properties.Count - candidateCleanType.OriginalType.Properties.Count) * 5; currentPenalty -= Math.Abs(obfType.OriginalType.Interfaces.Count - candidateCleanType.OriginalType.Interfaces.Count) * 35; var options = obfType.AssemblyContext.GlobalContext.Options; foreach (var obfuscatedField in obfType.OriginalType.Fields) { if (obfuscatedField.Name.IsObfuscated(options)) { var bestFieldScore = candidateCleanType.OriginalType.Fields.Max(it => TypeMatchWeight(obfuscatedField.FieldType, it.FieldType, options)); currentPenalty += bestFieldScore * (bestFieldScore < 0 ? 10 : 2); continue; } if (candidateCleanType.OriginalType.Fields.Any(it => it.Name == obfuscatedField.Name)) { currentPenalty += 10; } } foreach (var obfuscatedMethod in obfType.OriginalType.Methods) { if (obfuscatedMethod.Name.Contains(".ctor")) { continue; } if (obfuscatedMethod.Name.IsObfuscated(options)) { var bestMethodScore = candidateCleanType.OriginalType.Methods.Max(it => MethodSignatureMatchWeight(obfuscatedMethod, it, options)); currentPenalty += bestMethodScore * (bestMethodScore < 0 ? 10 : 1); continue; } if (candidateCleanType.OriginalType.Methods.Any(it => it.Name == obfuscatedMethod.Name)) { currentPenalty += obfuscatedMethod.Name.Length / 10 * 5 + 1; } } if (currentPenalty == bestPenalty) { bestMatch = null; } else if (currentPenalty > bestPenalty) { bestPenalty = currentPenalty; bestMatch = candidateCleanType; } } // if (bestPenalty < -100) // bestMatch = null; return(bestMatch, bestPenalty); }
public static bool TranslateMethod(MethodDefinition original, MethodDefinition target, TypeRewriteContext typeRewriteContext, AssemblyKnownImports imports) { if (!original.HasBody) { return(true); } var globalContext = typeRewriteContext.AssemblyContext.GlobalContext; foreach (var variableDefinition in original.Body.Variables) { var variableType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, variableDefinition.VariableType, imports); if (variableType == null) { return(false); } target.Body.Variables.Add(new VariableDefinition(variableType)); } var targetBuilder = target.Body.GetILProcessor(); foreach (var bodyInstruction in original.Body.Instructions) { if (bodyInstruction.OpCode.OperandType == OperandType.InlineField) { var fieldArg = (FieldReference)bodyInstruction.Operand; var fieldDeclarer = Pass80UnstripMethods.ResolveTypeInNewAssembliesRaw(globalContext, fieldArg.DeclaringType, imports); if (fieldDeclarer == null) { return(false); } var newField = fieldDeclarer.Resolve().Fields.SingleOrDefault(it => it.Name == fieldArg.Name); if (newField != null) { targetBuilder.Emit(bodyInstruction.OpCode, imports.Module.ImportReference(newField)); } else { if (bodyInstruction.OpCode == OpCodes.Ldfld || bodyInstruction.OpCode == OpCodes.Ldsfld) { var getterMethod = fieldDeclarer.Resolve().Properties.SingleOrDefault(it => it.Name == fieldArg.Name)?.GetMethod; if (getterMethod == null) { return(false); } targetBuilder.Emit(OpCodes.Call, imports.Module.ImportReference(getterMethod)); } else if (bodyInstruction.OpCode == OpCodes.Stfld || bodyInstruction.OpCode == OpCodes.Stsfld) { var setterMethod = fieldDeclarer.Resolve().Properties.SingleOrDefault(it => it.Name == fieldArg.Name)?.SetMethod; if (setterMethod == null) { return(false); } targetBuilder.Emit(OpCodes.Call, imports.Module.ImportReference(setterMethod)); } else { return(false); } } } else if (bodyInstruction.OpCode.OperandType == OperandType.InlineMethod) { var methodArg = (MethodReference)bodyInstruction.Operand; var methodDeclarer = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArg.DeclaringType, imports); if (methodDeclarer == null) { return(false); // todo: generic methods } var newReturnType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArg.ReturnType, imports); if (newReturnType == null) { return(false); } var newMethod = new MethodReference(methodArg.Name, newReturnType, methodDeclarer); newMethod.HasThis = methodArg.HasThis; foreach (var methodArgParameter in methodArg.Parameters) { var newParamType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArgParameter.ParameterType, imports); if (newParamType == null) { return(false); } var newParam = new ParameterDefinition(methodArgParameter.Name, methodArgParameter.Attributes, newParamType); newMethod.Parameters.Add(newParam); } targetBuilder.Emit(bodyInstruction.OpCode, imports.Module.ImportReference(newMethod)); } else if (bodyInstruction.OpCode.OperandType == OperandType.InlineType) { var targetType = (TypeReference)bodyInstruction.Operand; if (targetType is GenericParameter genericParam) { if (genericParam.Owner is TypeReference paramOwner) { var newTypeOwner = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, paramOwner, imports); if (newTypeOwner == null) { return(false); } targetType = newTypeOwner.GenericParameters.Single(it => it.Name == targetType.Name); } else { targetType = target.GenericParameters.Single(it => it.Name == targetType.Name); } } else { targetType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, targetType, imports); if (targetType == null) { return(false); } } if (bodyInstruction.OpCode == OpCodes.Castclass && !targetType.IsValueType) { targetBuilder.Emit(OpCodes.Call, imports.Module.ImportReference(new GenericInstanceMethod(imports.Il2CppObjectCast) { GenericArguments = { targetType } })); } else if (bodyInstruction.OpCode == OpCodes.Isinst && !targetType.IsValueType) { targetBuilder.Emit(OpCodes.Call, imports.Module.ImportReference(new GenericInstanceMethod(imports.Il2CppObjectTryCast) { GenericArguments = { targetType } })); } else { targetBuilder.Emit(bodyInstruction.OpCode, targetType); } } else if (bodyInstruction.OpCode.OperandType == OperandType.InlineSig) { // todo: rewrite sig if this ever happens in unity types return(false); } else if (bodyInstruction.OpCode.OperandType == OperandType.InlineTok) { var targetTok = (TypeReference)bodyInstruction.Operand; if (targetTok is GenericParameter genericParam) { if (genericParam.Owner is TypeReference paramOwner) { var newTypeOwner = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, paramOwner, imports); if (newTypeOwner == null) { return(false); } targetTok = newTypeOwner.GenericParameters.Single(it => it.Name == targetTok.Name); } else { targetTok = target.GenericParameters.Single(it => it.Name == targetTok.Name); } } else { targetTok = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, targetTok, imports); if (targetTok == null) { return(false); } } targetBuilder.Emit(OpCodes.Call, imports.Module.ImportReference(new GenericInstanceMethod(imports.LdTokUnstrippedImpl) { GenericArguments = { targetTok } })); } else { targetBuilder.Append(bodyInstruction); } } return(true); }
private static void EmitObjectStoreGeneric(ILProcessor body, TypeReference originalType, TypeReference newType, TypeRewriteContext enclosingType, int argumentIndex) { // input stack: target address // output: nothing var imports = enclosingType.AssemblyContext.Imports; body.Emit(OpCodes.Ldtoken, newType); body.Emit(OpCodes.Call, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == nameof(Type.GetTypeFromHandle)))); body.Emit(OpCodes.Dup); body.Emit(OpCodes.Callvirt, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == typeof(Type).GetProperty(nameof(Type.IsValueType)) !.GetMethod !.Name))); var finalNop = body.Create(OpCodes.Nop); var stringNop = body.Create(OpCodes.Nop); var valueTypeNop = body.Create(OpCodes.Nop); var storePointerNop = body.Create(OpCodes.Nop); body.Emit(OpCodes.Brtrue, valueTypeNop); body.Emit(OpCodes.Callvirt, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == typeof(Type).GetProperty(nameof(Type.FullName)) !.GetMethod !.Name))); body.Emit(OpCodes.Ldstr, "System.String"); body.Emit(OpCodes.Call, enclosingType.NewType.Module.ImportReference(TargetTypeSystemHandler.String.Methods.Single(it => it.Name == nameof(String.Equals) && it.IsStatic && it.Parameters.Count == 2))); body.Emit(OpCodes.Brtrue_S, stringNop); body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Box, newType); body.Emit(OpCodes.Isinst, imports.Il2CppObjectBase); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); body.Emit(OpCodes.Dup); body.Emit(OpCodes.Brfalse_S, storePointerNop); body.Emit(OpCodes.Dup); body.Emit(OpCodes.Call, imports.ObjectGetClass); body.Emit(OpCodes.Call, imports.ClassIsValueType); body.Emit(OpCodes.Brfalse_S, storePointerNop); body.Emit(OpCodes.Dup); var tempLocal = new VariableDefinition(imports.IntPtr); body.Body.Variables.Add(tempLocal); body.Emit(OpCodes.Stloc, tempLocal); body.Emit(OpCodes.Call, imports.ObjectUnbox); body.Emit(OpCodes.Ldloc, tempLocal); body.Emit(OpCodes.Call, imports.ObjectGetClass); body.Emit(OpCodes.Ldc_I4_0); body.Emit(OpCodes.Conv_U); body.Emit(OpCodes.Call, imports.ValueSizeGet); body.Emit(OpCodes.Cpblk); body.Emit(OpCodes.Br_S, finalNop); body.Append(storePointerNop); body.Emit(OpCodes.Stind_I); body.Emit(OpCodes.Br_S, finalNop); body.Append(stringNop); body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Box, newType); body.Emit(OpCodes.Isinst, imports.String); body.Emit(OpCodes.Call, imports.StringToNative); body.Emit(OpCodes.Stind_I); body.Emit(OpCodes.Br_S, finalNop); body.Append(valueTypeNop); body.Emit(OpCodes.Pop); // pop extra typeof(T) body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Stobj, newType); body.Append(finalNop); }
public static void EmitObjectStore(this ILProcessor body, TypeReference originalType, TypeReference newType, TypeRewriteContext enclosingType, int argumentIndex) { // input stack: target address // output: nothing if (originalType is GenericParameter) { EmitObjectStoreGeneric(body, originalType, newType, enclosingType, argumentIndex); return; } var imports = enclosingType.AssemblyContext.Imports; if (originalType.FullName == "System.String") { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Call, imports.StringToNative); body.Emit(OpCodes.Stobj, imports.IntPtr); } else if (originalType.IsValueType) { var typeSpecifics = enclosingType.AssemblyContext.GlobalContext.JudgeSpecificsByOriginalType(originalType); if (typeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct) { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Stobj, newType); } else { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); body.Emit(OpCodes.Call, imports.ObjectUnbox); var classPointerTypeRef = new GenericInstanceType(imports.Il2CppClassPointerStore) { GenericArguments = { newType } }; var classPointerFieldRef = new FieldReference(nameof(Il2CppClassPointerStore <int> .NativeClassPtr), imports.IntPtr, classPointerTypeRef); body.Emit(OpCodes.Ldsfld, enclosingType.NewType.Module.ImportReference(classPointerFieldRef)); body.Emit(OpCodes.Ldc_I4_0); body.Emit(OpCodes.Call, imports.ValueSizeGet); body.Emit(OpCodes.Cpblk); } } else { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); body.Emit(OpCodes.Stobj, imports.IntPtr); } }
private static void EmitPointerToObjectGeneric(ILProcessor body, TypeReference originalReturnType, TypeReference newReturnType, TypeRewriteContext enclosingType, Instruction loadPointer, bool extraDerefForNonValueTypes, bool unboxValueType) { var imports = enclosingType.AssemblyContext.Imports; body.Append(loadPointer); body.Emit(OpCodes.Ldtoken, newReturnType); body.Emit(OpCodes.Call, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == nameof(Type.GetTypeFromHandle)))); body.Emit(OpCodes.Dup); body.Emit(OpCodes.Callvirt, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == typeof(Type).GetProperty(nameof(Type.IsValueType)) !.GetMethod !.Name))); var finalNop = body.Create(OpCodes.Nop); var valueTypeNop = body.Create(OpCodes.Nop); var stringNop = body.Create(OpCodes.Nop); var normalRefTypeNop = body.Create(OpCodes.Nop); body.Emit(OpCodes.Brtrue, valueTypeNop); body.Emit(OpCodes.Callvirt, enclosingType.NewType.Module.ImportReference(imports.Type.Methods.Single(it => it.Name == typeof(Type).GetProperty(nameof(Type.FullName)) !.GetMethod !.Name))); body.Emit(OpCodes.Ldstr, "System.String"); body.Emit(OpCodes.Call, enclosingType.NewType.Module.ImportReference(TargetTypeSystemHandler.String.Methods.Single(it => it.Name == nameof(String.Equals) && it.IsStatic && it.Parameters.Count == 2))); body.Emit(OpCodes.Brtrue_S, stringNop); if (!unboxValueType) { var loadClassPointer = body.Create(OpCodes.Ldsfld, new FieldReference(nameof(Il2CppClassPointerStore <int> .NativeClassPtr), imports.IntPtr, enclosingType.NewType.Module.ImportReference( new GenericInstanceType(imports.Il2CppClassPointerStore) { GenericArguments = { newReturnType } }))); body.Append(loadClassPointer); body.Emit(OpCodes.Call, imports.ClassIsValueType); body.Emit(OpCodes.Brfalse, normalRefTypeNop); body.Emit(OpCodes.Pop); // pop object pointer body.Append(loadClassPointer); body.Append(loadPointer); body.Emit(OpCodes.Call, imports.ObjectBox); body.Append(normalRefTypeNop); } var createRealObject = body.Create(OpCodes.Newobj, new MethodReference(".ctor", imports.Void, imports.Il2CppObjectBase) { Parameters = { new ParameterDefinition(imports.IntPtr) }, HasThis = true }); if (extraDerefForNonValueTypes) { body.Emit(OpCodes.Ldind_I); } body.Emit(OpCodes.Dup); body.Emit(OpCodes.Brtrue_S, createRealObject); body.Emit(OpCodes.Pop); body.Emit(OpCodes.Ldnull); body.Emit(OpCodes.Br, finalNop); body.Append(createRealObject); body.Emit(OpCodes.Call, enclosingType.NewType.Module.ImportReference(new GenericInstanceMethod(imports.Il2CppObjectCast) { GenericArguments = { newReturnType } })); body.Emit(OpCodes.Br, finalNop); body.Append(stringNop); if (extraDerefForNonValueTypes) { body.Emit(OpCodes.Ldind_I); } body.Emit(OpCodes.Call, imports.StringFromNative); body.Emit(OpCodes.Isinst, newReturnType); // satisfy the verifier body.Emit(OpCodes.Br_S, finalNop); body.Append(valueTypeNop); body.Emit(OpCodes.Pop); // pop extra typeof(T) if (unboxValueType) { body.Emit(OpCodes.Call, imports.ObjectUnbox); } body.Emit(OpCodes.Ldobj, newReturnType); body.Append(finalNop); }
public static void EmitPointerToObject(this ILProcessor body, TypeReference originalReturnType, TypeReference convertedReturnType, TypeRewriteContext enclosingType, Instruction loadPointer, bool extraDerefForNonValueTypes, bool unboxValueType) { // input stack: not used // output stack: converted result if (originalReturnType is GenericParameter) { EmitPointerToObjectGeneric(body, originalReturnType, convertedReturnType, enclosingType, loadPointer, extraDerefForNonValueTypes, unboxValueType); return; } var imports = enclosingType.AssemblyContext.Imports; if (originalReturnType.FullName == "System.Void") { // do nothing } else if (originalReturnType.IsValueType) { if (convertedReturnType.IsValueType) { body.Append(loadPointer); if (unboxValueType) { body.Emit(OpCodes.Call, imports.ObjectUnbox); } body.Emit(OpCodes.Ldobj, convertedReturnType); } else { if (!unboxValueType) { var classPointerTypeRef = new GenericInstanceType(imports.Il2CppClassPointerStore) { GenericArguments = { convertedReturnType } }; var classPointerFieldRef = new FieldReference(nameof(Il2CppClassPointerStore <int> .NativeClassPtr), imports.IntPtr, classPointerTypeRef); body.Emit(OpCodes.Ldsfld, enclosingType.NewType.Module.ImportReference(classPointerFieldRef)); body.Append(loadPointer); body.Emit(OpCodes.Call, imports.ObjectBox); } else // already boxed { body.Append(loadPointer); } body.Emit(OpCodes.Newobj, new MethodReference(".ctor", imports.Void, convertedReturnType) { Parameters = { new ParameterDefinition(imports.IntPtr) }, HasThis = true }); } } else if (originalReturnType.FullName == "System.String") { body.Append(loadPointer); if (extraDerefForNonValueTypes) { body.Emit(OpCodes.Ldind_I); } body.Emit(OpCodes.Call, imports.StringFromNative); } else if (originalReturnType.IsArray && originalReturnType.GetElementType().IsGenericParameter) { body.Append(loadPointer); var actualReturnType = imports.Il2CppArrayBaseSelfSubst; var methodRef = new MethodReference(nameof(Il2CppArrayBase <int> .WrapNativeGenericArrayPointer), actualReturnType, convertedReturnType) { HasThis = false, Parameters = { new ParameterDefinition(imports.IntPtr) } }; body.Emit(OpCodes.Call, methodRef); } else { var createRealObject = body.Create(OpCodes.Newobj, new MethodReference(".ctor", imports.Void, convertedReturnType) { Parameters = { new ParameterDefinition(imports.IntPtr) }, HasThis = true }); var endNop = body.Create(OpCodes.Nop); body.Append(loadPointer); if (extraDerefForNonValueTypes) { body.Emit(OpCodes.Ldind_I); } body.Emit(OpCodes.Dup); body.Emit(OpCodes.Brtrue_S, createRealObject); body.Emit(OpCodes.Pop); body.Emit(OpCodes.Ldnull); body.Emit(OpCodes.Br, endNop); body.Append(createRealObject); body.Append(endNop); } }
public static void EmitObjectToPointer(this ILProcessor body, TypeReference originalType, TypeReference newType, TypeRewriteContext enclosingType, int argumentIndex, bool valueTypeArgument0IsAPointer, bool allowNullable, bool unboxNonBlittableType, out VariableDefinition refVariable) { // input stack: not used // output stack: IntPtr to either Il2CppObject or IL2CPP value type refVariable = null; if (originalType is GenericParameter) { EmitObjectToPointerGeneric(body, originalType, newType, enclosingType, argumentIndex, valueTypeArgument0IsAPointer, allowNullable, unboxNonBlittableType); return; } var imports = enclosingType.AssemblyContext.Imports; if (originalType is ByReferenceType) { if (newType.GetElementType().IsValueType) { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Conv_I); } else if (originalType.GetElementType().IsValueType) { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Ldind_Ref); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointerNotNull); } else { var pointerVar = new VariableDefinition(imports.IntPtr); refVariable = pointerVar; body.Body.Variables.Add(pointerVar); body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Ldind_Ref); if (originalType.FullName == "System.String") { body.Emit(OpCodes.Call, imports.StringToNative); } else { body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); } body.Emit(OpCodes.Stloc, pointerVar); body.Emit(OpCodes.Ldloca, pointerVar); body.Emit(OpCodes.Conv_I); } } else if (originalType.IsValueType) { if (newType.IsValueType) { if (argumentIndex == 0 && valueTypeArgument0IsAPointer) { body.Emit(OpCodes.Ldarg_0); } else { body.Emit(OpCodes.Ldarga, argumentIndex); } } else { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointerNotNull); if (unboxNonBlittableType) { body.Emit(OpCodes.Call, imports.ObjectUnbox); } } } else if (originalType.FullName == "System.String") { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Call, imports.StringToNative); } else { body.Emit(OpCodes.Ldarg, argumentIndex); body.Emit(OpCodes.Call, allowNullable ? imports.Il2CppObjectBaseToPointer : imports.Il2CppObjectBaseToPointerNotNull); } }
public static void GenerateInvokerMethodBody(MethodDefinition newMethod, FieldDefinition delegateField, TypeDefinition delegateType, TypeRewriteContext enclosingType, AssemblyKnownImports imports) { var body = newMethod.Body.GetILProcessor(); body.Emit(OpCodes.Ldsfld, delegateField); if (newMethod.HasThis) { body.Emit(OpCodes.Ldarg_0); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointerNotNull); } var argOffset = newMethod.HasThis ? 1 : 0; for (var i = 0; i < newMethod.Parameters.Count; i++) { var param = newMethod.Parameters[i]; var paramType = param.ParameterType; if (paramType.IsValueType || paramType.IsByReference && paramType.GetElementType().IsValueType) { body.Emit(OpCodes.Ldarg, i + argOffset); } else { body.EmitObjectToPointer(param.ParameterType, param.ParameterType, enclosingType, i + argOffset, false, true, true, out var refVar); if (refVar != null) { LogSupport.Warning($"Method {newMethod} has a reference-typed ref parameter, this will be ignored"); } } } body.Emit(OpCodes.Call, delegateType.Methods.Single(it => it.Name == "Invoke")); if (!newMethod.ReturnType.IsValueType) { var pointerVar = new VariableDefinition(imports.IntPtr); newMethod.Body.Variables.Add(pointerVar); body.Emit(OpCodes.Stloc, pointerVar); var loadInstr = body.Create(OpCodes.Ldloc, pointerVar); body.EmitPointerToObject(newMethod.ReturnType, newMethod.ReturnType, enclosingType, loadInstr, false, false); } body.Emit(OpCodes.Ret); }