diff --git a/NLog.config b/NLog.config index 2b900e3..fb8235a 100644 --- a/NLog.config +++ b/NLog.config @@ -14,6 +14,7 @@ + diff --git a/Torch/Managers/PatchManager/DecoratedMethod.cs b/Torch/Managers/PatchManager/DecoratedMethod.cs index 22e3073..310610b 100644 --- a/Torch/Managers/PatchManager/DecoratedMethod.cs +++ b/Torch/Managers/PatchManager/DecoratedMethod.cs @@ -99,14 +99,6 @@ namespace Torch.Managers.PatchManager public const string RESULT_PARAMETER = "__result"; public const string PREFIX_SKIPPED_PARAMETER = "__prefixSkipped"; -#pragma warning disable 649 - [ReflectedStaticMethod(Type = typeof(RuntimeHelpers), Name = "_CompileMethod", OverrideTypeNames = new[] { "System.IRuntimeMethodInfo" })] - private static Action _compileDynamicMethod; - [ReflectedMethod(Name = "GetMethodInfo")] - private static Func _getMethodInfo; - [ReflectedMethod(Name = "GetMethodDescriptor")] - private static Func _getMethodHandle; -#pragma warning restore 649 public DynamicMethod ComposePatchedMethod() { @@ -124,10 +116,7 @@ namespace Torch.Managers.PatchManager try { - // Force it to compile - RuntimeMethodHandle handle = _getMethodHandle.Invoke(method); - object runtimeMethodInfo = _getMethodInfo.Invoke(handle); - _compileDynamicMethod.Invoke(runtimeMethodInfo); + PatchUtilities.Compile(method); } catch { diff --git a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs index cf0dc34..755af14 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs @@ -426,16 +426,16 @@ namespace Torch.Managers.PatchManager.MSIL { case OperandType.InlineTok: Debug.Assert(Value is MethodBase || Value is Type || Value is FieldInfo, - $"Value {Value?.GetType()} doesn't match operand type"); + $"Value {Value?.GetType()} doesn't match operand type for {Instruction.OpCode}"); break; case OperandType.InlineType: - Debug.Assert(Value is Type, $"Value {Value?.GetType()} doesn't match operand type"); + Debug.Assert(Value is Type, $"Value {Value?.GetType()} doesn't match operand type for {Instruction.OpCode}"); break; case OperandType.InlineMethod: - Debug.Assert(Value is MethodBase, $"Value {Value?.GetType()} doesn't match operand type"); + Debug.Assert(Value is MethodBase, $"Value {Value?.GetType()} doesn't match operand type for {Instruction.OpCode}"); break; case OperandType.InlineField: - Debug.Assert(Value is FieldInfo, $"Value {Value?.GetType()} doesn't match operand type"); + Debug.Assert(Value is FieldInfo, $"Value {Value?.GetType()} doesn't match operand type for {Instruction.OpCode}"); break; default: throw new InvalidBranchException( diff --git a/Torch/Managers/PatchManager/PatchUtilities.cs b/Torch/Managers/PatchManager/PatchUtilities.cs index 0320742..46d2a29 100644 --- a/Torch/Managers/PatchManager/PatchUtilities.cs +++ b/Torch/Managers/PatchManager/PatchUtilities.cs @@ -3,10 +3,13 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using NLog; using Torch.Managers.PatchManager.MSIL; using Torch.Managers.PatchManager.Transpile; +using Torch.Utils; namespace Torch.Managers.PatchManager { @@ -36,5 +39,35 @@ namespace Torch.Managers.PatchManager { MethodTranspiler.EmitMethod(insn.ToList(), generator); } + + /// + /// Analyzes the integrity of a set of instructions. + /// + /// default logging level + /// instructions + public static void IntegrityAnalysis(LogLevel level, IReadOnlyList instructions) + { + MethodTranspiler.IntegrityAnalysis(level, instructions); + } + +#pragma warning disable 649 + [ReflectedStaticMethod(Type = typeof(RuntimeHelpers), Name = "_CompileMethod", OverrideTypeNames = new[] { "System.IRuntimeMethodInfo" })] + private static Action _compileDynamicMethod; + [ReflectedMethod(Name = "GetMethodInfo")] + private static Func _getMethodInfo; + [ReflectedMethod(Name = "GetMethodDescriptor")] + private static Func _getMethodHandle; +#pragma warning restore 649 + /// + /// Forces the given dynamic method to be compiled + /// + /// + public static void Compile(DynamicMethod method) + { + // Force it to compile + RuntimeMethodHandle handle = _getMethodHandle.Invoke(method); + object runtimeMethodInfo = _getMethodInfo.Invoke(handle); + _compileDynamicMethod.Invoke(runtimeMethodInfo); + } } } diff --git a/Torch/TorchBase.cs b/Torch/TorchBase.cs index a2e92db..eaccab9 100644 --- a/Torch/TorchBase.cs +++ b/Torch/TorchBase.cs @@ -353,10 +353,10 @@ namespace Torch Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}"); Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}"); + Managers.GetManager().LoadPlugins(); Game = new VRageGame(this, TweakGameSettings, SteamAppName, SteamAppId, Config.InstancePath, RunArgs); if (!Game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromMinutes(5))) Log.Warn("Failed to wait for game to be initialized"); - Managers.GetManager().LoadPlugins(); Managers.Attach(); _init = true; diff --git a/Torch/Utils/Reflected/ReflectedManager.cs b/Torch/Utils/Reflected/ReflectedManager.cs index f6d1180..1097423 100644 --- a/Torch/Utils/Reflected/ReflectedManager.cs +++ b/Torch/Utils/Reflected/ReflectedManager.cs @@ -5,12 +5,15 @@ using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; using NLog; using Sandbox.Engine.Multiplayer; using Torch.API; +using Torch.Managers.PatchManager; using Torch.Utils.Reflected; +using PropertyAttributes = System.Reflection.PropertyAttributes; namespace Torch.Utils { @@ -53,14 +56,16 @@ namespace Torch.Utils { #if DEBUG if (Process(field)) - _log?.Trace($"Field {field.DeclaringType?.FullName}#{field.Name} = {field.GetValue(null) ?? "null"}"); + _log?.Trace( + $"Field {field.DeclaringType?.FullName}#{field.Name} = {field.GetValue(null) ?? "null"}"); #else Process(field); #endif } catch (Exception e) { - _log?.Error(e.InnerException ?? e, $"Unable to fill {field.DeclaringType?.FullName}#{field.Name}. {(e.InnerException ?? e).Message}"); + _log?.Error(e.InnerException ?? e, + $"Unable to fill {field.DeclaringType?.FullName}#{field.Name}. {(e.InnerException ?? e).Message}"); } } } @@ -121,6 +126,7 @@ namespace Torch.Utils new Func(() => new ReflectedEventReplacer(reflectedEventReplacer))); return true; } + return false; } @@ -142,8 +148,8 @@ namespace Torch.Utils break; case ReflectedPropertyInfoAttribute rpia: info = GetFieldPropRecursive(rpia.Type, rpia.Name, - BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, - (type, name, bindingFlags) => type.GetProperty(name, bindingFlags)); + BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, + (type, name, bindingFlags) => type.GetProperty(name, bindingFlags)); if (info == null) throw new ArgumentException($"Unable to find property {rpia.Type.FullName}#{rpia.Name}"); break; @@ -151,8 +157,8 @@ namespace Torch.Utils if (rmia.Parameters != null) { info = rmia.Type.GetMethod(rmia.Name, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, - null, CallingConventions.Any, rmia.Parameters, null); + BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, CallingConventions.Any, rmia.Parameters, null); if (info == null) throw new ArgumentException( $"Unable to find method {rmia.Type.FullName}#{rmia.Name}({string.Join(", ", rmia.Parameters.Select(x => x.FullName))})"); @@ -160,17 +166,21 @@ namespace Torch.Utils else { info = rmia.Type.GetMethod(rmia.Name, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (info == null) throw new ArgumentException( $"Unable to find method {rmia.Type.FullName}#{rmia.Name}"); } - if (rmia.ReturnType != null && !rmia.ReturnType.IsAssignableFrom(((MethodInfo)info).ReturnType)) - throw new ArgumentException($"Method {rmia.Type.FullName}#{rmia.Name} has return type {((MethodInfo)info).ReturnType.FullName}, expected {rmia.ReturnType.FullName}"); + + if (rmia.ReturnType != null && !rmia.ReturnType.IsAssignableFrom(((MethodInfo) info).ReturnType)) + throw new ArgumentException( + $"Method {rmia.Type.FullName}#{rmia.Name} has return type {((MethodInfo) info).ReturnType.FullName}, expected {rmia.ReturnType.FullName}"); break; } + if (info == null) - throw new ArgumentException($"Unable to find member info for {attr.GetType().Name}[{attr.Type.FullName}#{attr.Name}"); + throw new ArgumentException( + $"Unable to find member info for {attr.GetType().Name}[{attr.Type.FullName}#{attr.Name}"); field.SetValue(null, info); } @@ -222,7 +232,7 @@ namespace Torch.Utils argExp[i] = paramExp[i]; field.SetValue(null, Expression.Lambda(Expression.Call(methodInstance, argExp), paramExp) - .Compile()); + .Compile()); } else field.SetValue(null, Delegate.CreateDelegate(field.FieldType, methodInstance)); @@ -243,12 +253,14 @@ namespace Torch.Utils : (Expression) paramExp[0]; field.SetValue(null, Expression.Lambda(Expression.Call(instanceExp, methodInstance, argExp), paramExp) - .Compile()); - _log.Trace($"Reflecting field {field.DeclaringType?.FullName}#{field.Name} with {methodInstance.DeclaringType?.FullName}#{methodInstance.Name}"); + .Compile()); + _log.Trace( + $"Reflecting field {field.DeclaringType?.FullName}#{field.Name} with {methodInstance.DeclaringType?.FullName}#{methodInstance.Name}"); } } - internal static T GetFieldPropRecursive(Type baseType, string name, BindingFlags flags, Func getter) where T : class + internal static T GetFieldPropRecursive(Type baseType, string name, BindingFlags flags, + Func getter) where T : class { while (baseType != null) { @@ -296,7 +308,8 @@ namespace Torch.Utils trueType = parameters[0].ParameterType; } else - throw new ArgumentException($"Field attribute type {attr.GetType().FullName} is invalid", nameof(field)); + throw new ArgumentException($"Field attribute type {attr.GetType().FullName} is invalid", + nameof(field)); BindingFlags bindingFlags = (isStatic ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.NonPublic | @@ -307,35 +320,77 @@ namespace Torch.Utils GetFieldPropRecursive(trueType, trueName, bindingFlags, (a, b, c) => a.GetProperty(b, c)); if (sourceField == null && sourceProperty == null) throw new ArgumentException( - $"Unable to find field or property for {trueName} in {trueType.FullName} or its base types", nameof(field)); + $"Unable to find field or property for {trueName} in {trueType.FullName} or its base types", + nameof(field)); + var sourceType = sourceField?.FieldType ?? sourceProperty.PropertyType; + bool isSetter = attr is ReflectedSetterAttribute; + if (sourceProperty != null && isSetter && !sourceProperty.CanWrite) + throw new InvalidOperationException( + $"Can't create setter for readonly property {trueName} in {trueType.FullName}"); + + if (sourceProperty != null && !isSetter && !sourceProperty.CanRead) + throw new InvalidOperationException( + $"Can't create getter for writeonly property {trueName} in {trueType.FullName}"); + + var dynMethod = new DynamicMethod((isSetter ? "set" : "get") + "_" + trueType.FullName + "." + trueName, + delegateMethod.ReturnType, parameters.Select(x => x.ParameterType).ToArray(), + typeof(ReflectedManager).Module, true); + ILGenerator il = dynMethod.GetILGenerator(); - ParameterExpression[] paramExp = parameters.Select(x => Expression.Parameter(x.ParameterType)).ToArray(); - Expression instanceExpr = null; if (!isStatic) - { - instanceExpr = trueType == paramExp[0].Type ? (Expression)paramExp[0] : Expression.Convert(paramExp[0], trueType); - } + EmitThis(il, parameters[0].ParameterType, trueType); - MemberExpression fieldExp = sourceField != null - ? Expression.Field(instanceExpr, sourceField) - : Expression.Property(instanceExpr, sourceProperty); - Expression impl; - if (attr is ReflectedSetterAttribute) + if (isSetter) { - var valParam = paramExp[isStatic ? 0 : 1]; - var valExpr = (Expression)valParam; - var setType = sourceField?.FieldType ?? sourceProperty.PropertyType; - if (valParam.Type != setType) - valExpr = Expression.Convert(valExpr, setType); - impl = Expression.Block(Expression.Assign(fieldExp, valExpr), Expression.Default(typeof(void))); + int val = isStatic ? 0 : 1; + il.Emit(OpCodes.Ldarg, val); + EmitCast(il, parameters[val].ParameterType, sourceType); + if (sourceProperty != null) + il.Emit(sourceProperty.SetMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt, + sourceProperty.SetMethod); + else + il.Emit(isStatic ? OpCodes.Stsfld : OpCodes.Stfld, sourceField); } else { - impl = fieldExp; + if (sourceProperty != null) + il.Emit(sourceProperty.GetMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt, + sourceProperty.GetMethod); + else + il.Emit(isStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, sourceField); + EmitCast(il, sourceType, delegateMethod.ReturnType); } - field.SetValue(null, Expression.Lambda(impl, paramExp).Compile()); - _log.Trace($"Reflecting field {field.DeclaringType?.FullName}#{field.Name} with {field.DeclaringType?.FullName}#{field.Name}"); + il.Emit(OpCodes.Ret); + + field.SetValue(null, dynMethod.CreateDelegate(field.FieldType)); + _log.Trace( + $"Reflecting field {field.DeclaringType?.FullName}#{field.Name} with {field.DeclaringType?.FullName}#{field.Name}"); } + + #region IL Utils + + private static void EmitThis(ILGenerator il, Type argType, Type privateType) + { + il.Emit(OpCodes.Ldarg_0); + // pointers? + EmitCast(il, argType, privateType); + } + + private static void EmitCast(ILGenerator il, Type from, Type to) + { + if (from.IsValueType && !to.IsValueType) + il.Emit(OpCodes.Box, from); + else if (!from.IsValueType && to.IsValueType) + { + il.Emit(OpCodes.Unbox, to); + return; + } + + if (from != to && (from.IsAssignableFrom(to) || to.IsAssignableFrom(from))) + il.Emit(OpCodes.Castclass, to); + } + + #endregion } -} +} \ No newline at end of file diff --git a/Torch/VRageGame.cs b/Torch/VRageGame.cs index c0ac111..20265d0 100644 --- a/Torch/VRageGame.cs +++ b/Torch/VRageGame.cs @@ -28,6 +28,7 @@ using VRage.FileSystem; using VRage.Game; using VRage.Game.SessionComponents; using VRage.GameServices; +using VRage.Network; using VRage.Plugins; using VRage.Steam; using VRage.Utils; @@ -53,6 +54,10 @@ namespace Torch [ReflectedMethod(Name = "Unload", TypeName = "Sandbox.Game.Audio.MyMusicController, Sandbox.Game")] private static readonly Action _musicControllerUnload; + +// [ReflectedGetter(Name = "UpdateLayerDescriptors", Type = typeof(MyReplicationServer))] +// private static readonly Func _layerSettings; + #pragma warning restore 649 private readonly TorchBase _torch; @@ -195,6 +200,9 @@ namespace Torch MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint"); } +// var layers = _layerSettings(); +// layers[layers.Length - 1].Radius *= 4; + _game = new SpaceEngineersGame(_runArgs); }