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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using Acornima;
using Acornima.Ast;
using Jint;

namespace XFESystem.Browser.Tests;

public sealed class PackagedScriptParserTests
{
    [Theory]
    [InlineData("function f(a=2,...rest){let x=a;return ()=>++x+rest.length}var g=f(40,1);g()+g()")]
    [InlineData("class A{#x=40;static count=0;constructor(){A.count++}get value(){return this.#x+A.count}}new A().value+new A().value")]
    [InlineData("function f(a){arguments[0]=42;return a}f(1)")]
    [InlineData("'use strict';function f(a){arguments[0]=42;return a}f(1)")]
    [InlineData("function f(){if(true){function g(){return 42}}return g()}f()")]
    [InlineData("let x=1;function f({a=x}={}){let x=2;return a}f()")]
    [InlineData("function* gen(){try{yield 40}finally{yield 2}}Array.from(gen()).join(',')")]
    [InlineData("var r=/x/g;var a=[r.test('xx'),r.lastIndex];a.join(',')")]
    [InlineData("var a=[];for(let i=0;i<3;i++)a.push(()=>i);a.map(f=>f()).join(',')")]
    [InlineData("var value=40;({value}).value+2")]
    [InlineData("function f(a){try{throw a}finally{a++}}try{f(42)}catch(e){e}")]
    [InlineData("const f=()=>({get value(){return 42}});f().value")]
    [InlineData("var a={value:40};a.value++;a['value']++;a?.value")]
    [InlineData("class A{get value(){return 40}}class B extends A{get value(){return super.value+2}}new B().value")]
    public void LazyAnalysisMatchesPinnedEagerEngineAcrossFreshEngines(string source)
    {
        string expected = new Engine().Evaluate(Engine.PrepareScript(source)).ToString();
        var prepared = PackagedScriptParser.Parse(source, "package:semantics", 10_000, out int nodes);
        Assert.True(prepared.IsValid); Assert.InRange(nodes, 1, 10_000);
        for (int instance = 0; instance < 3; instance++)
            Assert.Equal(expected, new Engine().Evaluate(prepared).ToString());
    }

    [Fact]
    public void UnusedFunctionsAreValidatedButTheirRuntimeAnalysisWaitsForUse()
    {
        var prepared = PackagedScriptParser.Parse("function used(a){return a+2}function unused(a){return a*2}used(40)", "package:lazy", 1000, out _);
        var used = Assert.IsType<FunctionDeclaration>(prepared.Program!.Body[0]);
        var unused = Assert.IsType<FunctionDeclaration>(prepared.Program.Body[1]);
        Assert.Null(used.UserData); Assert.Null(unused.UserData);
        var engine = new Engine(); Assert.Equal("42", engine.Evaluate(prepared).ToString());
        Assert.NotNull(used.UserData); Assert.Null(unused.UserData);
        Assert.Equal("42", engine.Evaluate("unused(21)").ToString());
        Assert.NotNull(unused.UserData);
        Assert.ThrowsAny<ParseErrorException>(() => PackagedScriptParser.Parse("function unused(){let a;let a;}", "package:early-error", 1000, out _));
        Assert.ThrowsAny<ParseErrorException>(() => PackagedScriptParser.Parse("function unused(){return /[/;}", "package:bad-regexp", 1000, out _));
    }

    [Fact]
    public void ParserRetainsSourceLocationsAndExplicitRegexpTimeoutWithoutCompiledRegex()
    {
        var prepared = PackagedScriptParser.Parse("\n /x+/g;", "package:locations", 1000, out _);
        var statement = Assert.IsAssignableFrom<ExpressionStatement>(prepared.Program!.Body[0]);
        var literal = Assert.IsType<RegExpLiteral>(statement.Expression);
        Assert.Null(literal.Value); // Pattern is validated now; conversion is first-use work.
        var regex = Assert.IsType<Jint.Native.JsRegExp>(new Engine().Evaluate(prepared)).Value;
        Assert.Equal(TimeSpan.FromMilliseconds(100), regex.MatchTimeout);
        Assert.False(regex.Options.HasFlag(System.Text.RegularExpressions.RegexOptions.Compiled));
        Assert.Equal("package:locations", literal.Location.SourceFile);
        Assert.Equal(2, literal.Location.Start.Line);
        Assert.Equal(new Version(4, 11, 0, 0), typeof(Engine).Assembly.GetName().Version);
    }
}