/// <summary> /// Sets the given local to its default value in the given IL generator. /// </summary> /// <param name="local">Local to set to default</param> /// <returns>Instructions</returns> public static IEnumerable <MsilInstruction> SetToDefault(this MsilLocal local) { Debug.Assert(local.Type != null); if (local.Type.IsEnum || local.Type.IsPrimitive) { if (local.Type == typeof(float)) { yield return(new MsilInstruction(OpCodes.Ldc_R4).InlineValue(0f)); } else if (local.Type == typeof(double)) { yield return(new MsilInstruction(OpCodes.Ldc_R8).InlineValue(0d)); } else if (local.Type == typeof(long) || local.Type == typeof(ulong)) { yield return(new MsilInstruction(OpCodes.Ldc_I8).InlineValue(0L)); } else { yield return(new MsilInstruction(OpCodes.Ldc_I4).InlineValue(0)); } yield return(new MsilInstruction(OpCodes.Stloc).InlineValue(local)); } else if (local.Type.IsValueType) // struct { yield return(new MsilInstruction(OpCodes.Ldloca).InlineValue(local)); yield return(new MsilInstruction(OpCodes.Initobj).InlineValue(local.Type)); } else // class { yield return(new MsilInstruction(OpCodes.Ldnull)); yield return(new MsilInstruction(OpCodes.Stloc).InlineValue(local)); } }
private IEnumerable <MsilInstruction> EmitPatched(Func <Type, bool, MsilLocal> declareLocal) { var methodBody = _method.GetMethodBody(); Debug.Assert(methodBody != null, "Method body is null"); foreach (var localVar in methodBody.LocalVariables) { Debug.Assert(localVar.LocalType != null); declareLocal(localVar.LocalType, localVar.IsPinned); } var instructions = new List <MsilInstruction>(); var specialVariables = new Dictionary <string, MsilLocal>(); var labelAfterOriginalContent = new MsilLabel(); var labelSkipMethodContent = new MsilLabel(); Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); MsilLocal resultVariable = null; if (returnType != typeof(void)) { if (Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER) || Prefixes.Any(x => x.ReturnType == typeof(bool))) { resultVariable = declareLocal(returnType, false); } } if (resultVariable != null) { instructions.AddRange(resultVariable.SetToDefault()); } MsilLocal prefixSkippedVariable = null; if (Prefixes.Count > 0 && Suffixes.Any(x => x.GetParameters() .Any(y => y.Name.Equals(PREFIX_SKIPPED_PARAMETER)))) { prefixSkippedVariable = declareLocal(typeof(bool), false); specialVariables.Add(PREFIX_SKIPPED_PARAMETER, prefixSkippedVariable); } if (resultVariable != null) { specialVariables.Add(RESULT_PARAMETER, resultVariable); } // Create special variables foreach (var m in Prefixes.Concat(Suffixes)) { foreach (var param in m.GetParameters()) { if (param.Name.StartsWith(LOCAL_PARAMETER)) { var requiredType = param.ParameterType.IsByRef ? param.ParameterType.GetElementType() : param.ParameterType; if (specialVariables.TryGetValue(param.Name, out var existingParam)) { if (existingParam.Type != requiredType) { throw new ArgumentException( $"Trying to use injected local {param.Name} for {m.DeclaringType?.FullName}#{m.ToString()} with type {requiredType} but a local with the same name already exists with type {existingParam.Type}", param.Name); } } else { specialVariables.Add(param.Name, declareLocal(requiredType, false)); } } } } foreach (MethodInfo prefix in Prefixes) { instructions.AddRange(EmitMonkeyCall(prefix, specialVariables)); if (prefix.ReturnType == typeof(bool)) { instructions.Add(new MsilInstruction(OpCodes.Brfalse).InlineTarget(labelSkipMethodContent)); } else if (prefix.ReturnType != typeof(void)) { throw new Exception( $"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}"); } } instructions.AddRange(MethodTranspiler.Transpile(_method, (x) => declareLocal(x, false), Transpilers, labelAfterOriginalContent)); instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelAfterOriginalContent)); if (resultVariable != null) { instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(resultVariable)); } var notSkip = new MsilLabel(); instructions.Add(new MsilInstruction(OpCodes.Br).InlineTarget(notSkip)); instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelSkipMethodContent)); if (prefixSkippedVariable != null) { instructions.Add(new MsilInstruction(OpCodes.Ldc_I4_1)); instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(prefixSkippedVariable)); } instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(notSkip)); foreach (MethodInfo suffix in Suffixes) { instructions.AddRange(EmitMonkeyCall(suffix, specialVariables)); if (suffix.ReturnType != typeof(void)) { throw new Exception($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}"); } } if (resultVariable != null) { instructions.Add(new MsilInstruction(OpCodes.Ldloc).InlineValue(resultVariable)); } instructions.Add(new MsilInstruction(OpCodes.Ret)); var result = MethodTranspiler.Transpile(_method, instructions, (x) => declareLocal(x, false), PostTranspilers, null).ToList(); if (result.Last().OpCode != OpCodes.Ret) { result.Add(new MsilInstruction(OpCodes.Ret)); } return(result); }
private static IEnumerable <MsilInstruction> TranspilerForUpdate <T>(IEnumerable <MsilInstruction> insn, Func <Type, MsilLocal> __localCreator, MethodBase __methodBase) { MethodInfo profilerCall = null; if (typeof(IMyEntity).IsAssignableFrom(typeof(T))) { profilerCall = ProfilerData.GetEntityProfiler; } else if (typeof(MyEntityComponentBase).IsAssignableFrom(typeof(T)) || typeof(T) == typeof(IMyGameLogicComponent)) { profilerCall = ProfilerData.GetEntityComponentProfiler; } else if (typeof(MyCubeGridSystems) == typeof(T)) { profilerCall = ProfilerData.GetGridSystemProfiler; } else if (typeof(MySessionComponentBase) == typeof(T)) { profilerCall = ProfilerData.GetSessionComponentProfiler; } else { _log.Warn($"Trying to profile unknown target {typeof(T)}"); } MsilLocal profilerEntry = profilerCall != null ? __localCreator(typeof(SlimProfilerEntry)) : null; var usedLocals = new List <MsilLocal>(); var tmpArgument = new Dictionary <Type, Stack <MsilLocal> >(); var foundAny = false; foreach (MsilInstruction i in insn) { if (profilerCall != null && (i.OpCode == OpCodes.Call || i.OpCode == OpCodes.Callvirt) && ShouldProfileMethodCall <T>((i.Operand as MsilOperandInline <MethodBase>)?.Value)) { MethodBase target = ((MsilOperandInline <MethodBase>)i.Operand).Value; ParameterInfo[] pams = target.GetParameters(); usedLocals.Clear(); foreach (ParameterInfo pam in pams) { if (!tmpArgument.TryGetValue(pam.ParameterType, out var stack)) { tmpArgument.Add(pam.ParameterType, stack = new Stack <MsilLocal>()); } MsilLocal local = stack.Count > 0 ? stack.Pop() : __localCreator(pam.ParameterType); usedLocals.Add(local); yield return(local.AsValueStore()); } _log.Debug($"Attaching profiling to {target?.DeclaringType?.FullName}#{target?.Name} in {__methodBase.DeclaringType?.FullName}#{__methodBase.Name} targeting {typeof(T)}"); yield return(new MsilInstruction(OpCodes.Dup)); // duplicate the object the update is called on if (typeof(MyCubeGridSystems) == typeof(T) && __methodBase.DeclaringType == typeof(MyCubeGridSystems)) { yield return(new MsilInstruction(OpCodes.Ldarg_0)); yield return(new MsilInstruction(OpCodes.Ldfld).InlineValue(_gridSystemsCubeGrid)); } yield return(new MsilInstruction(OpCodes.Call).InlineValue(profilerCall)); // consume object the update is called on yield return(new MsilInstruction(OpCodes.Dup)); // Duplicate profiler entry for brnull yield return(profilerEntry.AsValueStore()); // store the profiler entry for later var skipProfilerOne = new MsilLabel(); yield return(new MsilInstruction(OpCodes.Brfalse).InlineTarget(skipProfilerOne)); // Brfalse == Brnull { yield return(profilerEntry.AsValueLoad()); // start the profiler yield return(new MsilInstruction(OpCodes.Call).InlineValue(ProfilerData.ProfilerEntryStart)); } // consumes from the first Dup yield return(new MsilInstruction(OpCodes.Nop).LabelWith(skipProfilerOne)); for (int j = usedLocals.Count - 1; j >= 0; j--) { yield return(usedLocals[j].AsValueLoad()); tmpArgument[usedLocals[j].Type].Push(usedLocals[j]); } yield return(i); var skipProfilerTwo = new MsilLabel(); yield return(profilerEntry.AsValueLoad()); yield return(new MsilInstruction(OpCodes.Brfalse).InlineTarget(skipProfilerTwo)); // Brfalse == Brnull { yield return(profilerEntry.AsValueLoad()); // stop the profiler yield return(new MsilInstruction(OpCodes.Call).InlineValue(ProfilerData.ProfilerEntryStop)); } yield return(new MsilInstruction(OpCodes.Nop).LabelWith(skipProfilerTwo)); foundAny = true; continue; } yield return(i); } if (!foundAny) { _log.Warn($"Didn't find any update profiling targets for target {typeof(T)} in {__methodBase.DeclaringType?.FullName}#{__methodBase.Name}"); } }