XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Security.Cryptography;
using System.Text.Json;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace XFESystem.Tools.JintCleanupPatch;

public static partial class Program
{
    private const string BoundMarker = "XFESystem.JintBoundCallableTargetsV1";
    private const string OriginalJintSha256 = "acf8a1a9f98aa771c95c2b15b9b58f21de8b77f027e2a8d5ca92597e87752d9d";

    private static void ValidateBoundInput(string path, bool hostOnly)
    {
        if (hostOnly) VerifyHostPrivateCopy(path);
        else VerifyPrivateCopy(path);
        using var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters { InMemory = true });
        if (assembly.Name.Name != "Jint" || assembly.Name.Version != new Version(4, 11, 0, 0))
            throw new InvalidOperationException("The callable target patch only supports pinned Jint 4.11.0.");
        var hash = Hash(path);
        if (assembly.MainModule.Types.Any(type => type.FullName == BoundMarker))
        {
            var receiptPath = path + ".XfeBind.json";
            if (!File.Exists(receiptPath)) throw new InvalidOperationException("A callable patch marker without its receipt is not trusted.");
            using var receipt = JsonDocument.Parse(File.ReadAllText(receiptPath));
            if (receipt.RootElement.GetProperty("AfterSha256").GetString() != hash)
                throw new InvalidOperationException("The marked Jint assembly differs from its callable patch receipt.");
            if (receipt.RootElement.GetProperty("Version").GetInt32() != 1 ||
                receipt.RootElement.GetProperty("Kind").GetString() != "ProjectLocalJintBoundCallableTargets" ||
                receipt.RootElement.GetProperty("PinnedOriginalSha256").GetString() != OriginalJintSha256)
                throw new InvalidOperationException("The callable patch receipt does not identify the pinned original and patch version.");
            if (assembly.MainModule.Types.Any(type => type.FullName == WeakMarker) &&
                (!receipt.RootElement.TryGetProperty("WeakCollectionsPatchVersion", out var weakVersion) || weakVersion.GetInt32() != 1))
                throw new InvalidOperationException("The owned weak collections marker has no matching verification receipt.");
            if(assembly.MainModule.Types.Any(type=>type.FullName==WeakMarker)) ValidateWeakCollections(assembly.MainModule);
            if (assembly.MainModule.Types.Any(type => type.FullName.StartsWith("XFESystem.JintOwnedEngineWeakCollections", StringComparison.Ordinal)))
            {
                if (!assembly.MainModule.Types.Any(type => type.FullName == EngineWeakMarker) ||
                    !receipt.RootElement.TryGetProperty("EngineWeakCollectionsPatchVersion", out var engineVersion) || engineVersion.GetInt32() != 1)
                    throw new InvalidOperationException("The Engine weak cache marker has no matching verification receipt.");
                ValidateEngineWeakCollections(assembly.MainModule);
            }
            else if (receipt.RootElement.TryGetProperty("EngineWeakCollectionsPatchVersion", out _) ||
                assembly.MainModule.GetType("Jint.Engine").Fields.Any(field => field.Name is "_emptyShapes" or "_privateElementStore" &&
                    field.FieldType is GenericInstanceType generic && generic.ElementType.FullName != OriginalWeakTable))
                throw new InvalidOperationException("A partial Engine weak cache patch is not trusted.");
            return;
        }
        if (hash != OriginalJintSha256)
        {
            if (hostOnly || !assembly.MainModule.Types.Any(type => type.FullName == Marker))
                throw new InvalidOperationException("An unmarked callable patch input must match the pinned original Jint SHA256.");
            using var cleanup = JsonDocument.Parse(File.ReadAllText(path + ".XfeCleanup.json"));
            if (cleanup.RootElement.GetProperty("BeforeSha256").GetString() != OriginalJintSha256 ||
                cleanup.RootElement.GetProperty("AfterSha256").GetString() != hash)
                throw new InvalidOperationException("The native cleanup input has no valid pinned-original receipt chain.");
        }
        _ = BoundShapes(assembly.MainModule);
    }

    private static int PatchBoundTargets(string path, bool hostOnly)
    {
        ValidateBoundInput(path, hostOnly);
        using var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters { InMemory = true });
        bool alreadyBound = assembly.MainModule.Types.Any(type => type.FullName == BoundMarker);
        if (alreadyBound && assembly.MainModule.Types.Any(type => type.FullName == EngineWeakMarker))
        { Console.WriteLine("XFE_JINT_BOUND_TARGETS_ALREADY_READY|" + Hash(path)); return 0; }
        var before = Hash(path);
        var (call, instanceOf) = alreadyBound ? (null, null) : BoundShapes(assembly.MainModule);
        var callable = assembly.MainModule.GetType("Jint.Native.ICallable")
            ?? throw new InvalidOperationException("Pinned ICallable type is missing.");
        var callableCall = callable.Methods.Single(method => method.Name == "Call" && method.Parameters.Count == 2);
        var objectInstance = assembly.MainModule.GetType("Jint.Native.Object.ObjectInstance")!;
        var jsValue = assembly.MainModule.GetType("Jint.Native.JsValue")!;
        var hasInstance = jsValue.Methods.Single(method => method.Name == "OrdinaryHasInstance" && method.Parameters.Count == 1);
        if (!alreadyBound)
        {
            RewriteTarget(call!, callable, callableCall);
            RewriteTarget(instanceOf!, objectInstance, hasInstance);
            assembly.MainModule.Types.Add(new TypeDefinition("XFESystem", "JintBoundCallableTargetsV1", TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed, assembly.MainModule.TypeSystem.Object));
        }
        if (!assembly.MainModule.Types.Any(type => type.FullName == WeakMarker)) PatchWeakCollections(assembly.MainModule);
        PatchEngineWeakCollections(assembly.MainModule);
        var temporary = path + ".XfeBind.tmp";
        assembly.Write(temporary); File.Move(temporary, path, true);
        var receipt = new
        {
            Kind = "ProjectLocalJintBoundCallableTargets", Version = 1, JintVersion = "4.11.0", Path = path,
            BeforeSha256 = before, AfterSha256 = Hash(path), PinnedOriginalSha256 = OriginalJintSha256,
            GlobalPackageCacheModified = false, HostOnly = hostOnly,
            Source = "https://github.com/sebastienros/jint/blob/v4.11.0/Jint/Native/Function/BindFunction.cs",
            Methods = new[] { "BindFunction.ICallable.Call", "BindFunction.OrdinaryHasInstance" },
            WeakCollectionsPatchVersion = 1,
            EngineWeakCollectionsPatchVersion = 1,
            WeakCollectionStorage = "JsWeakMap/JsWeakSet retain their intrinsic algorithms; private tables use per-engine explicit weak/dependent handles.",
            Change = "Call dispatches through ICallable; instanceof dispatches through ObjectInstance/JsValue. Bound arguments, bound this, constructors and original exceptions remain unchanged."
        };
        File.WriteAllText(path + ".XfeBind.json", JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true }));
        Console.WriteLine("XFE_JINT_BOUND_TARGETS_READY|" + receipt.AfterSha256);
        return 0;
    }

    private static (MethodDefinition Call, MethodDefinition InstanceOf) BoundShapes(ModuleDefinition module)
    {
        var type = module.GetType("Jint.Native.Function.BindFunction")
            ?? throw new InvalidOperationException("Pinned BindFunction type is missing.");
        var call = type.Methods.Single(method => method.Name.EndsWith("ICallable.Call", StringComparison.Ordinal));
        var instanceOf = type.Methods.Single(method => method.Name == "OrdinaryHasInstance");
        foreach (var method in new[] { call, instanceOf })
        {
            if (!method.HasBody || method.Body.ExceptionHandlers.Count != 0 ||
                method.Body.Instructions.Count(instruction => instruction.OpCode == OpCodes.Isinst && instruction.Operand is TypeReference reference && reference.FullName == "Jint.Native.Function.Function") != 1 ||
                method.Body.Variables.Any(variable => variable.VariableType.FullName == "Jint.Native.Function.Function") ||
                method.Body.Instructions.Count(instruction => instruction.OpCode == OpCodes.Callvirt && instruction.Operand is MethodReference reference &&
                    reference.Name == (method == call ? "Call" : "OrdinaryHasInstance")) != 1 ||
                !method.Body.Instructions.Any(instruction => instruction.OpCode == OpCodes.Ldstr && instruction.Operand is string text &&
                    text == (method == call ? "Bind must be called on a function" : "Right-hand side of 'instanceof' is not callable")))
                throw new InvalidOperationException("Pinned BindFunction method shape changed: " + method.FullName + "\n" +
                    string.Join("\n", method.Body.Instructions.Select(instruction => instruction.ToString())) + "\nLocals: " + string.Join(",", method.Body.Variables.Select(variable => variable.VariableType.FullName)));
        }
        return (call, instanceOf);
    }

    private static void RewriteTarget(MethodDefinition method, TypeReference targetType, MethodReference targetMethod)
    {
        foreach (var variable in method.Body.Variables.Where(variable => variable.VariableType.FullName == "Jint.Native.Function.Function")) variable.VariableType = targetType;
        foreach (var instruction in method.Body.Instructions)
        {
            if (instruction.OpCode == OpCodes.Isinst && instruction.Operand is TypeReference type && type.FullName == "Jint.Native.Function.Function") instruction.Operand = targetType;
            if (instruction.OpCode == OpCodes.Callvirt && instruction.Operand is MethodReference target && target.Name == targetMethod.Name) instruction.Operand = targetMethod;
        }
    }

    private static void VerifyHostPrivateCopy(string path)
    {
        string? taskRoot = Directory.GetCurrentDirectory();
        while (taskRoot is not null && !File.Exists(Path.Combine(taskRoot, "XFESystem.slnx"))) taskRoot = Path.GetDirectoryName(taskRoot);
        if (taskRoot is null) throw new ArgumentException("Run the host patch from the XFESystem workspace.");
        var relative = Path.GetRelativePath(taskRoot, path).Replace('\\', '/');
        if (Path.GetFileName(path) != "Jint.dll" || relative.StartsWith("../", StringComparison.Ordinal) ||
            !relative.Contains("/obj/", StringComparison.OrdinalIgnoreCase) || !relative.Contains("/XfeJintHost/", StringComparison.OrdinalIgnoreCase))
            throw new ArgumentException("Only a workspace project's private obj/XfeJintHost Jint.dll copy may be host-patched.");
        for (string? candidate = path; candidate is not null && !candidate.Equals(taskRoot, StringComparison.OrdinalIgnoreCase); candidate = Path.GetDirectoryName(candidate))
            if ((File.GetAttributes(candidate) & FileAttributes.ReparsePoint) != 0) throw new ArgumentException("A private host Jint copy cannot use a symbolic link or junction.");
    }
}