using System.Diagnostics; using System.Reflection; using Jint; using Jint.Native; using Jint.Runtime.Interop; using XFESystem.Browser; // Host-only diagnostic. Calls the original intrinsic directly, under the // existing evaluation budget; never replaces regex results or resets limits. internal sealed class MonacoRegexProfile { private sealed class Work { public long Calls, Ticks, InputCharacters, FailedSuffixHits; } private readonly Dictionary patterns = new(StringComparer.Ordinal); internal void Install(BrowserService browser) { var runtime = (JavaScriptRuntime)typeof(BrowserService).GetField("runtime", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(browser)!; var engine = (Engine)typeof(JavaScriptRuntime).GetField("engine", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(runtime)!; var prototype = engine.GetValue("RegExp").AsObject().Get("prototype").AsObject(); var original = (Func)prototype.Get("exec").ToObject()!; var hitCounter = typeof(Jint.Native.JsRegExp).GetField("_xfeNoMatchHits", BindingFlags.Instance | BindingFlags.NonPublic); prototype.Set("exec", new ClrFunction(engine, "exec", (receiver, arguments) => { var regex = receiver.AsObject(); string key = regex.Get("source").AsString() + "/" + regex.Get("flags").AsString(); if (!patterns.TryGetValue(key, out var work)) patterns.Add(key, work = new()); work.Calls++; if (arguments.Length > 0 && arguments[0].IsString()) work.InputCharacters += arguments[0].AsString().Length; long started = Stopwatch.GetTimestamp(); long beforeHits = (long?)hitCounter?.GetValue(regex) ?? 0; var result = original(receiver, arguments); work.Ticks += Stopwatch.GetTimestamp() - started; work.FailedSuffixHits += ((long?)hitCounter?.GetValue(regex) ?? 0) - beforeHits; return result; }), false); } internal object[] Results() => patterns.OrderByDescending(pair => pair.Value.Ticks).Take(20).Select(pair => (object)new { Pattern = pair.Key, pair.Value.Calls, pair.Value.InputCharacters, pair.Value.FailedSuffixHits, Milliseconds = pair.Value.Ticks * 1000.0 / Stopwatch.Frequency }).ToArray(); }