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

XFESystem

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

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

namespace XFESystem.Browser;

/// <summary>
/// Fully validates syntax, while leaving Jint's function/declaration analysis on
/// its normal first-use path. Large packages contain many unused function bodies.
/// The AST and analysis contain no realm; interpreter handlers remain engine owned.
/// </summary>
internal static class PackagedScriptParser
{
    // Obtain the pinned engine's own grammar and regexp adapter. Prepared scripts
    // otherwise default to compiled regex and a ten-second regexp timeout.
    private static readonly ParserOptions options = Engine.PrepareScript(string.Empty,
        options: new ScriptPreparationOptions
        {
            FoldConstants = false,
            ParsingOptions = new() { CompileRegex = false, RegexTimeout = TimeSpan.FromMilliseconds(100) }
        }).ParserOptions! with { OnNode = null };

    internal static Prepared<Script> Parse(string source, string key, int maximumNodes, out int nodes)
    {
        var preparation = new SyntaxPreparation(maximumNodes);
        var parser = new Parser(options with { OnNode = preparation.Visit });
        var script = parser.ParseScript(source, sourceFile: key, strict: false);
        nodes = preparation.Count;
        return PreparedFactory<Script>.Create(script, options);
    }

    // Jint 4.11.0 exposes evaluation of Prepared<Script>, but its AST wrapper
    // constructor is internal. This exact signature is pinned by Packages.lock;
    // UnsafeAccessor emits a direct AOT call, with no reflection or layout cast.
    private static class PreparedFactory<TProgram> where TProgram : Acornima.Ast.Program
    {
        [UnsafeAccessor(UnsafeAccessorKind.Constructor)]
        internal static extern Prepared<TProgram> Create(TProgram program, ParserOptions parserOptions);
    }

    // The same immutable identifier memento used by Jint's AstAnalyzer. Only its
    // name/key is shared; scope resolution and mutable inline caches remain on
    // each engine's interpreter nodes. This signature also pins Jint 4.11.0.
    [UnsafeAccessor(UnsafeAccessorKind.Constructor)]
    [return: UnsafeAccessorType("Jint.Runtime.Environments.Environment+BindingName, Jint")]
    private static extern object CreateBindingName(string name);

    private sealed class SyntaxPreparation(int maximum)
    {
        private readonly Dictionary<string, object> bindingNames = new(StringComparer.Ordinal);
        private readonly Dictionary<string, JsString> propertyNames = new(StringComparer.Ordinal);
        internal int Count { get; private set; }
        internal void Visit(Node node, in OnNodeContext context)
        {
            // An over-budget script is still parsed and executed, but not cached.
            if (Count > maximum) return;
            Count++;
            if (node is Identifier identifier)
            {
                if (!bindingNames.TryGetValue(identifier.Name, out var binding))
                    bindingNames.Add(identifier.Name, binding = CreateBindingName(identifier.Name));
                node.UserData = binding;
            }
            else if (node is MemberExpression { Computed: false, Property: Identifier property })
            {
                if (!propertyNames.TryGetValue(property.Name, out var key))
                    propertyNames.Add(property.Name, key = new JsString(property.Name));
                node.UserData = key;
            }
        }
    }
}