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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Reflection;
using Jint;
using Jint.Native;
using XFESystem.Collections;

namespace XFESystem.JintCleanupPatch.Tests;

public sealed class RegexSearchTests
{
    private static long Hits(JsValue value) => (long)typeof(JsRegExp).GetField("_xfeNoMatchHits", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(value)!;
    private static Engine Create() => new(options => { options.DisableStringCompilation(); options.Constraints.RegexTimeout = TimeSpan.FromMilliseconds(50); });

    [Fact]
    public void RepeatedFailedSuffixSkipsSearchButEarlierStartStillFindsMatches()
    {
        var engine = Create();
        try
        {
            engine.Execute("const input='baaa',r=/b/gu;r.lastIndex=1;r.exec(input);r.lastIndex=2;r.exec(input)");
            Assert.Equal(1, Hits(engine.Evaluate("r")));
            Assert.Equal("b,0,1", engine.Evaluate("r.lastIndex=0;const m=r.exec(input);[m[0],m.index,r.lastIndex].join(',')").AsString());
        }
        finally { ((IWeakCollectionOwner)(object)engine).WeakCollections.Release(); }
    }

    [Theory]
    [InlineData("const r=/a/uy;r.exec('ba');r.lastIndex=1;const m=r.exec('ba');[m[0],m.index,r.lastIndex].join(',')", "a,1,2")]
    [InlineData("const r=/z/gu;r.exec('aaa');r.lastIndex=2;const a=r.exec('aaaz');[a[0],a.index,r.lastIndex].join(',')", "z,3,4")]
    [InlineData("const r=/z/gu;r.exec('aaa');r.compile('a','gu');const m=r.exec('aaa');[m[0],m.index,r.lastIndex].join(',')", "a,0,1")]
    [InlineData("const r=/z/gu;r.exec('aaa');r.exec=function(){return ['custom']};r.exec('aaa')[0]", "custom")]
    [InlineData("const r=/(?<x>a)|(b)/dgu;r.exec('cc');r.lastIndex=0;const a=r.exec('ab');a[0]='changed';r.lastIndex=1;const b=r.exec('ab');JSON.stringify([b[0],b[1],b[2],b.groups.x,b.indices[0],r.lastIndex])", "[\"b\",null,\"b\",null,[1,2],2]")]
    [InlineData("const r=/(?<=😀)x/gu;r.exec('😀y');r.lastIndex=1;r.exec('😀y');r.lastIndex=1;const m=r.exec('😀x');[m[0],m.index,r.lastIndex].join(',')", "x,2,3")]
    [InlineData("const r=/z/gu;r.exec('aaa');let reads=0;r.lastIndex={valueOf(){reads++;return 1}};r.exec('aaa');[reads,r.lastIndex].join(',')", "1,0")]
    public void StickyInputCompileCapturesUnicodeAndObservableLastIndexKeepOriginalSemantics(string source, string expected)
    {
        var engine = Create();
        try { Assert.Equal(expected, engine.Evaluate(source).AsString()); }
        finally { ((IWeakCollectionOwner)(object)engine).WeakCollections.Release(); }
    }

    [Fact]
    public void RetainedInputIsBoundedAndRegexInstancesDoNotShareSearchFacts()
    {
        var engine = Create();
        try
        {
            engine.Execute("const input='a'.repeat(131073),r=/z/u;r.exec(input);r.exec(input);const other=/z/u;other.exec('aaa')");
            Assert.Equal(0, Hits(engine.Evaluate("r")));
            Assert.Null(typeof(JsRegExp).GetField("_xfeNoMatchInput", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(engine.Evaluate("r")));
            Assert.Equal(0, Hits(engine.Evaluate("other")));
        }
        finally { ((IWeakCollectionOwner)(object)engine).WeakCollections.Release(); }
    }

    [Fact]
    public void TimedOutSearchesRemainFailuresAndAreNeverMemoized()
    {
        var engine = Create();
        try
        {
            engine.Execute("const r=/(a+)+$/u,input='a'.repeat(32)+'!'");
            for (int attempt = 0; attempt < 2; attempt++)
                Assert.Throws<System.Text.RegularExpressions.RegexMatchTimeoutException>(() => engine.Evaluate("r.exec(input)"));
            Assert.Equal(0, Hits(engine.Evaluate("r")));
        }
        finally { ((IWeakCollectionOwner)(object)engine).WeakCollections.Release(); }
    }
}