public static void InstrumentType(Cecil.TypeDefinition type) { foreach (var method in type.Methods) { if (!method.HasBody) { continue; } if (method.IsConstructor) { continue; } if (method.IsStatic) { continue; } var fieldName = method.GetHotpatchFieldName(); if (type.Fields.Any(f => f.Name == fieldName)) { Debug.LogWarningFormat("{0} already instrumented", method.FullName); continue; } if (method.HasGenericParameters) { Debug.LogWarningFormat("{0} cannot be instrumented - generic parameters", method.FullName); continue; } if (method.Parameters.Any(p => p.IsOut || p.ParameterType.IsByReference)) { Debug.LogWarningFormat("{0} cannot be instrumented - out/ref arguments", method.FullName); continue; } //Create delegate type and static field in class var del = type.Module.Import(typeof(RefEmit.DynamicMethod)); var field = new Cecil.FieldDefinition(fieldName, Cecil.FieldAttributes.Private, del); field.IsStatic = true; type.Fields.Add(field); //Instrument method code to check for delegate field and call it instead var objType = type.Module.TypeSystem.Object; var voidType = type.Module.TypeSystem.Void; var objArrayType = type.Module.Import(typeof(object[])); var dynamicInvoke = type.Module.Import(typeof(RefEmit.DynamicMethod).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) })); var argArrayVar = new Cecil.Cil.VariableDefinition(objArrayType); method.Body.Variables.Add(argArrayVar); var ilproc = method.Body.GetILProcessor(); var firstins = ilproc.Body.Instructions[0]; Action <Cecil.Cil.Instruction> emit = (ins) => ilproc.InsertBefore(firstins, ins); //var writeLineMethod = typeof(UnityEngine.Debug).GetMethod("Log", new Type[]{typeof(string)}); //var writeLine = type.Module.Import(writeLineMethod); //emit(ilproc.Create(OpCodes.Ldstr, "Hello " + method.FullName)); //emit(ilproc.Create(OpCodes.Call, writeLine)); //Check if delegate field is not null emit(ilproc.Create(OpCodes.Ldsfld, field)); emit(ilproc.Create(OpCodes.Ldnull)); emit(ilproc.Create(OpCodes.Ceq)); //If null, branch to original function emit(ilproc.Create(OpCodes.Brtrue, firstins)); //Create object[] args array emit(ilproc.Create(OpCodes.Ldc_I4, method.Parameters.Count + 1)); emit(ilproc.Create(OpCodes.Newarr, objType)); emit(ilproc.Create(OpCodes.Stloc, argArrayVar)); //Store this at args[0] emit(ilproc.Create(OpCodes.Ldloc, argArrayVar)); emit(ilproc.Create(OpCodes.Ldc_I4_0)); //index[0] emit(ilproc.Create(OpCodes.Ldarg_0)); //arg[0] (this) emit(ilproc.Create(OpCodes.Stelem_Ref)); //store ref for (int i = 0; i < method.Parameters.Count; i++) { var p = method.Parameters[i]; //Store parameters in args array emit(ilproc.Create(OpCodes.Ldloc, argArrayVar)); emit(ilproc.Create(OpCodes.Ldc_I4, i + 1)); //index[i + 1] emit(ilproc.Create(OpCodes.Ldarg, i + 1)); //arg[i + 1] if (p.ParameterType.IsValueType) { emit(ilproc.Create(OpCodes.Box, p.ParameterType)); } emit(ilproc.Create(OpCodes.Stelem_Ref)); //store ref } //Call delegate emit(ilproc.Create(OpCodes.Ldsfld, field)); emit(ilproc.Create(OpCodes.Ldarg_0)); //arg[0] (this) emit(ilproc.Create(OpCodes.Ldloc, argArrayVar)); emit(ilproc.Create(OpCodes.Callvirt, dynamicInvoke)); //Handle return value if (method.ReturnType != voidType) { if (method.ReturnType.IsValueType) { emit(ilproc.Create(OpCodes.Unbox_Any, method.ReturnType)); } } else { //Discard dynamicInvoke result emit(ilproc.Create(OpCodes.Pop)); } //Return emit(ilproc.Create(OpCodes.Ret)); } }
public static void InstrumentType(Cecil.TypeDefinition type) { foreach (var method in type.Methods) { if (!method.HasBody) { continue; } if (method.IsConstructor) { continue; } if (method.IsStatic) { continue; } var fieldName = method.GetHotpatchFieldName(); if (type.Fields.Any(f => f.Name == fieldName)) { Debug.LogWarningFormat("{0} already instrumented", method.FullName); continue; } if (method.HasGenericParameters) { Debug.LogWarningFormat("{0} cannot be instrumented - generic parameters", method.FullName); continue; } if (method.Parameters.Any(p => p.IsOut || p.ParameterType.IsByReference)) { Debug.LogWarningFormat("{0} cannot be instrumented - out/ref arguments", method.FullName); continue; } //Create delegate type and static field in class var del = type.Module.Import(typeof(RefEmit.DynamicMethod)); var field = new Cecil.FieldDefinition(fieldName, Cecil.FieldAttributes.Private, del); field.IsStatic = true; type.Fields.Add(field); //Instrument method code to check for delegate field and call it instead var objType = type.Module.TypeSystem.Object; var voidType = type.Module.TypeSystem.Void; var objArrayType = type.Module.Import(typeof(object[])); var dynamicInvoke = type.Module.Import(typeof(RefEmit.DynamicMethod).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) })); var argArrayVar = new Cecil.Cil.VariableDefinition(objArrayType); method.Body.Variables.Add(argArrayVar); var ilproc = method.Body.GetILProcessor(); var firstins = ilproc.Body.Instructions[0]; Action<Cecil.Cil.Instruction> emit = (ins) => ilproc.InsertBefore(firstins, ins); //var writeLineMethod = typeof(UnityEngine.Debug).GetMethod("Log", new Type[]{typeof(string)}); //var writeLine = type.Module.Import(writeLineMethod); //emit(ilproc.Create(OpCodes.Ldstr, "Hello " + method.FullName)); //emit(ilproc.Create(OpCodes.Call, writeLine)); //Check if delegate field is not null emit(ilproc.Create(OpCodes.Ldsfld, field)); emit(ilproc.Create(OpCodes.Ldnull)); emit(ilproc.Create(OpCodes.Ceq)); //If null, branch to original function emit(ilproc.Create(OpCodes.Brtrue, firstins)); //Create object[] args array emit(ilproc.Create(OpCodes.Ldc_I4, method.Parameters.Count + 1)); emit(ilproc.Create(OpCodes.Newarr, objType)); emit(ilproc.Create(OpCodes.Stloc, argArrayVar)); //Store this at args[0] emit(ilproc.Create(OpCodes.Ldloc, argArrayVar)); emit(ilproc.Create(OpCodes.Ldc_I4_0)); //index[0] emit(ilproc.Create(OpCodes.Ldarg_0)); //arg[0] (this) emit(ilproc.Create(OpCodes.Stelem_Ref)); //store ref for (int i = 0; i < method.Parameters.Count; i++) { var p = method.Parameters[i]; //Store parameters in args array emit(ilproc.Create(OpCodes.Ldloc, argArrayVar)); emit(ilproc.Create(OpCodes.Ldc_I4, i + 1)); //index[i + 1] emit(ilproc.Create(OpCodes.Ldarg, i + 1)); //arg[i + 1] if (p.ParameterType.IsValueType) { emit(ilproc.Create(OpCodes.Box, p.ParameterType)); } emit(ilproc.Create(OpCodes.Stelem_Ref)); //store ref } //Call delegate emit(ilproc.Create(OpCodes.Ldsfld, field)); emit(ilproc.Create(OpCodes.Ldarg_0)); //arg[0] (this) emit(ilproc.Create(OpCodes.Ldloc, argArrayVar)); emit(ilproc.Create(OpCodes.Callvirt, dynamicInvoke)); //Handle return value if (method.ReturnType != voidType) { if (method.ReturnType.IsValueType) { emit(ilproc.Create(OpCodes.Unbox_Any, method.ReturnType)); } } else { //Discard dynamicInvoke result emit(ilproc.Create(OpCodes.Pop)); } //Return emit(ilproc.Create(OpCodes.Ret)); } }