Files
Torch/Torch/Patches/ScriptCompilerPatch.cs
2023-02-21 22:43:30 +07:00

115 lines
5.2 KiB
C#

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.CodeAnalysis;
using ParallelTasks;
using ProtoBuf;
using Torch.Managers.PatchManager;
using Torch.Managers.PatchManager.MSIL;
using Torch.Utils;
using VRage.FileSystem;
using VRage.Scripting;
namespace Torch.Patches
{
[PatchShim]
public static class ScriptCompilerPatch
{
[ReflectedMethodInfo(typeof(MyScriptWhitelist), "Register", Parameters = new[] {typeof(MyWhitelistTarget), typeof(INamespaceSymbol), typeof(Type)})]
private static MethodInfo Register1Method = null!;
[ReflectedMethodInfo(typeof(MyScriptWhitelist), "Register", Parameters = new[] {typeof(MyWhitelistTarget), typeof(ITypeSymbol), typeof(Type)})]
private static MethodInfo Register2Method = null!;
public static void Patch(PatchContext context)
{
context.GetPattern(typeof(MyScriptWhitelist).GetConstructor(new[] {typeof(MyScriptCompiler)}))
.AddPrefix(nameof(WhitelistCtorPrefix));
context.GetPattern(Type.GetType("VRage.Scripting.MyVRageScriptingInternal, VRage.Scripting", true).GetMethod("Initialize"))
.AddPrefix(nameof(InitializePrefix));
context.GetPattern(typeof(MyScriptWhitelist).GetNestedType("MyWhitelistBatch", BindingFlags.NonPublic)!
.GetMethod("AllowMembers")).AddPrefix(nameof(AllowMembersPrefix));
context.GetPattern(Register1Method).AddTranspiler(nameof(RegisterTranspiler));
context.GetPattern(Register2Method).AddTranspiler(nameof(RegisterTranspiler));
}
private static void WhitelistCtorPrefix(MyScriptCompiler scriptCompiler)
{
var baseDir = new FileInfo(typeof(Type).Assembly.Location).DirectoryName!;
scriptCompiler.AddReferencedAssemblies(
typeof(Type).Assembly.Location,
typeof(LinkedList<>).Assembly.Location,
typeof(Regex).Assembly.Location,
typeof(Enumerable).Assembly.Location,
typeof(ConcurrentBag<>).Assembly.Location,
typeof(ImmutableArray).Assembly.Location,
typeof(PropertyChangedEventArgs).Assembly.Location,
typeof(TypeConverter).Assembly.Location,
typeof(System.Diagnostics.TraceSource).Assembly.Location,
typeof(System.Security.Policy.Evidence).Assembly.Location,
Path.Combine(baseDir, "System.Xml.ReaderWriter.dll"),
Path.Combine(baseDir, "netstandard.dll"),
Path.Combine(baseDir, "System.Runtime.dll"),
Path.Combine(MyFileSystem.ExePath, "ProtoBuf.Net.Core.dll"),
Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll"),
Path.Combine(MyFileSystem.ExePath, "Sandbox.Common.dll"),
Path.Combine(MyFileSystem.ExePath, "Sandbox.Graphics.dll"),
Path.Combine(MyFileSystem.ExePath, "VRage.dll"),
Path.Combine(MyFileSystem.ExePath, "VRage.Library.dll"),
Path.Combine(MyFileSystem.ExePath, "VRage.Math.dll"),
Path.Combine(MyFileSystem.ExePath, "VRage.Game.dll"),
Path.Combine(MyFileSystem.ExePath, "VRage.Render.dll"),
Path.Combine(MyFileSystem.ExePath, "VRage.Input.dll"),
Path.Combine(MyFileSystem.ExePath, "SpaceEngineers.ObjectBuilders.dll"),
Path.Combine(MyFileSystem.ExePath, "SpaceEngineers.Game.dll"));
}
private static bool InitializePrefix(Thread updateThread, Type[] referencedTypes, string[] symbols)
{
MyModWatchdog.Init(updateThread);
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(referencedTypes);
MyScriptCompiler.Static.AddConditionalCompilationSymbols(symbols);
using var batch = MyScriptCompiler.Static.Whitelist.OpenBatch();
batch.AllowTypes(MyWhitelistTarget.ModApi, typeof(ConcurrentQueue<>));
return false;
}
[SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
private static void AllowMembersPrefix(ref MemberInfo[] members)
{
if (members.Any(b => b is null))
members = members.Where(b => b is { }).ToArray();
}
private static IEnumerable<MsilInstruction> RegisterTranspiler(IEnumerable<MsilInstruction> instructions)
{
var ins = instructions.ToList();
var throwIns = ins.FindAll(b => b.OpCode == OpCodes.Throw).Select(b => ins.IndexOf(b));
foreach (var index in throwIns)
{
var i = index;
do
{
ins[i] = new(OpCodes.Nop);
} while (ins[--i].OpCode.FlowControl != FlowControl.Cond_Branch);
ins[index] = new(OpCodes.Ret);
}
return ins;
}
}
}