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.Nodes;
using Jint;
using Mono.Cecil;
using Mono.Cecil.Cil;
using XFESystem.Collections;
using Patch=XFESystem.Tools.JintCleanupPatch.Program;

namespace XFESystem.JintCleanupPatch.Tests;

public sealed class EngineWeakCachePatchTests
{
    [Fact]
    public void OriginalPrivateCachesUseTheSameDirectEngineOwnerAndAnExplicitDictionaryFactory()
    {
        using var assembly=AssemblyDefinition.ReadAssembly(typeof(Engine).Assembly.Location);
        var module=assembly.MainModule;Assert.NotNull(module.GetType("XFESystem.JintOwnedEngineWeakCollectionsV1"));
        var engine=module.GetType("Jint.Engine");
        foreach(string name in new[]{"_emptyShapes","_privateElementStore"})
            Assert.Equal(typeof(OwnedWeakTable<,>).FullName,((GenericInstanceType)engine.Fields.Single(field=>field.Name==name).FieldType).ElementType.FullName);
        var factory=engine.Methods.Single(method=>method.Name=="XfeCreatePrivateElementDictionary");
        Assert.Equal(2,factory.Body.Instructions.Count);Assert.Equal(OpCodes.Newobj,factory.Body.Instructions[0].OpCode);
        Assert.Equal(OpCodes.Ret,factory.Body.Instructions[1].OpCode);
        Assert.DoesNotContain(engine.Methods.Where(method=>method.HasBody).SelectMany(method=>method.Body.Instructions),instruction=>
            instruction.Operand is MethodReference target&&target.DeclaringType.FullName=="System.Activator");
        // The unrelated interop cache is intentionally outside this verified patch.
        Assert.StartsWith("System.Runtime.CompilerServices.ConditionalWeakTable`2",engine.Fields.Single(field=>field.Name=="_objectWrapperCache").FieldType.FullName);
    }

    [Fact]
    public void ThrowingPublicConfigureCallbackReleasesAlreadyAllocatedScopeAndPreservesExceptionIdentity()
    {
        WeakCollectionScope? scope=null;var expected=new InvalidOperationException("known constructor failure");
        var actual=Assert.Throws<InvalidOperationException>(()=>new Engine((engine,options)=>
        {
            scope=((IWeakCollectionOwner)(object)engine).WeakCollections;
            var table=new OwnedWeakTable<object,object>(scope);table.Add(new object(),new object());
            Assert.Equal(1,scope.Lifetime.Entries);throw expected;
        }));
        Assert.Same(expected,actual);Assert.NotNull(scope);
        Assert.Equal(0,scope.Lifetime.Tables);Assert.Equal(0,scope.Lifetime.Entries);
        Assert.Equal(scope.Lifetime.Created,scope.Lifetime.Disposed);
        Assert.Equal(scope.Lifetime.TableHandlesCreated,scope.Lifetime.TableHandlesDisposed);
        Assert.Throws<ObjectDisposedException>(()=>new OwnedWeakTable<object,object>(scope));
    }

    [Theory]
    [InlineData("MissingHandler")]
    [InlineData("WrongFactory")]
    [InlineData("OldField")]
    [InlineData("MissingVersion")]
    [InlineData("UnsupportedVersion")]
    [InlineData("WrongOwner")]
    public void PartialEngineCachePatchCannotBeTrustedByMerelyUpdatingItsHashReceipt(string damage)
    {
        string path=PrivatePath();File.Copy(typeof(Engine).Assembly.Location,path);
        var receipt=JsonNode.Parse(File.ReadAllText(typeof(Engine).Assembly.Location+".XfeBind.json"))!;
        using(var assembly=AssemblyDefinition.ReadAssembly(path,new ReaderParameters{InMemory=true}))
        {
            var engine=assembly.MainModule.GetType("Jint.Engine");
            if(damage=="MissingHandler")engine.Methods.Single(method=>method.IsConstructor&&method.Parameters.Count==2).Body.ExceptionHandlers.Clear();
            if(damage=="WrongFactory")engine.Methods.Single(method=>method.Name=="XfeCreatePrivateElementDictionary").Body.Instructions[0].OpCode=OpCodes.Ldnull;
            if(damage=="WrongOwner")
            {
                var creation=engine.Methods.Single(method=>method.Name=="GetEmptyShape").Body.Instructions.Single(instruction=>instruction.OpCode==OpCodes.Newobj&&
                    instruction.Operand is MethodReference target&&target.DeclaringType.FullName.StartsWith("XFESystem.Collections.OwnedWeakTable`2",StringComparison.Ordinal));
                creation.Previous.Previous.OpCode=OpCodes.Ldnull;
            }
            if(damage=="OldField")
            {
                var field=engine.Fields.Single(field=>field.Name=="_emptyShapes");var old=(GenericInstanceType)field.FieldType;
                var restored=new GenericInstanceType(assembly.MainModule.ImportReference(typeof(System.Runtime.CompilerServices.ConditionalWeakTable<,>)));
                foreach(var argument in old.GenericArguments)restored.GenericArguments.Add(argument);field.FieldType=restored;
            }
            assembly.Write(path);
        }
        if(damage=="MissingVersion")receipt.AsObject().Remove("EngineWeakCollectionsPatchVersion");
        if(damage=="UnsupportedVersion")receipt["EngineWeakCollectionsPatchVersion"]=99;
        string hash=Hash(path);receipt["AfterSha256"]=hash;File.WriteAllText(path+".XfeBind.json",receipt.ToJsonString());
        Assert.Throws<InvalidOperationException>(()=>Patch.Main([path,"--bind-only"]));Assert.Equal(hash,Hash(path));
    }

    [Fact]
    public void PreviouslyVerifiedMapOnlyPatchUpgradesWithoutDuplicatingOwnersOrChangingGlobalPackages()
    {
        string packages=Environment.GetEnvironmentVariable("NUGET_PACKAGES")??Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),".nuget","packages");
        string original=Path.Combine(packages,"jint","4.11.0","lib","net10.0","Jint.dll");
        string originalHash=Hash(original),path=PrivatePath();
        using(var assembly=AssemblyDefinition.ReadAssembly(original,new ReaderParameters{InMemory=true}))
        {
            var module=assembly.MainModule;
            // Reproduce the previous verified storage stage on the pinned binary.
            typeof(Patch).GetMethod("PatchWeakCollections",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic)!.Invoke(null,[module]);
            var type=module.GetType("Jint.Native.Function.BindFunction");
            foreach(var method in type.Methods.Where(method=>method.Name.EndsWith("ICallable.Call",StringComparison.Ordinal)||method.Name=="OrdinaryHasInstance"))
            {
                bool call=method.Name.EndsWith("ICallable.Call",StringComparison.Ordinal);
                var target=module.GetType(call?"Jint.Native.ICallable":"Jint.Native.JsValue").Methods.Single(item=>item.Name==(call?"Call":"OrdinaryHasInstance")&&item.Parameters.Count==(call?2:1));
                foreach(var instruction in method.Body.Instructions)
                {
                    if(instruction.OpCode==OpCodes.Isinst&&instruction.Operand is TypeReference reference&&reference.FullName=="Jint.Native.Function.Function")
                        instruction.Operand=module.GetType(call?"Jint.Native.ICallable":"Jint.Native.Object.ObjectInstance");
                    if(instruction.OpCode==OpCodes.Callvirt&&instruction.Operand is MethodReference referenceMethod&&referenceMethod.Name==target.Name)instruction.Operand=target;
                }
            }
            module.Types.Add(new("XFESystem","JintBoundCallableTargetsV1",TypeAttributes.Abstract|TypeAttributes.Sealed|TypeAttributes.NotPublic,module.TypeSystem.Object));
            assembly.Write(path);
        }
        File.WriteAllText(path+".XfeBind.json",new JsonObject{["Kind"]="ProjectLocalJintBoundCallableTargets",["Version"]=1,
            ["PinnedOriginalSha256"]=originalHash,["AfterSha256"]=Hash(path),["WeakCollectionsPatchVersion"]=1}.ToJsonString());
        Patch.Main([path,"--bind-only"]);string upgraded=Hash(path);Patch.Main([path,"--bind-only"]);Assert.Equal(upgraded,Hash(path));
        using var current=AssemblyDefinition.ReadAssembly(path);Assert.NotNull(current.MainModule.GetType("XFESystem.JintOwnedEngineWeakCollectionsV1"));
        Assert.Single(current.MainModule.GetType("Jint.Engine").Fields,field=>field.Name=="_xfeWeakCollections");
        Assert.Equal(originalHash,Hash(original));
    }

    private static string PrivatePath()
    {
        string? root=AppContext.BaseDirectory;while(root is not null&&!File.Exists(Path.Combine(root,"XFESystem.slnx")))root=Path.GetDirectoryName(root);
        Assert.NotNull(root);string directory=Path.Combine(root,"build","EngineWeakFixture-"+Guid.NewGuid().ToString("N"),"obj","XfeJintHost");
        Directory.CreateDirectory(directory);return Path.Combine(directory,"Jint.dll");
    }
    private static string Hash(string path)=>Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(path)));
}