From 0bba4f54377663cd448fd5a306cd09f54cb1ee72 Mon Sep 17 00:00:00 2001 From: Kishikawa Katsumi Date: Thu, 9 Apr 2026 04:55:06 +0900 Subject: [PATCH 1/2] Fix multiple bugs: debugger event listener leak, pipe deadlock, group count, and HTML errors - Fix event listener leak in debugger by registering input handler once in init() - Fix string vs number comparison for backtrack count in debugger - Fix pipe deadlock by reading stdout/stderr before waitUntilExit() - Fix incorrect groupCount increment for non-capturing groups in ExpressionParser - Fix unclosed thead tag and stray closing span in index.html --- Public/index.html | 3 ++- Public/js/app.js | 11 ++++++----- Sources/App/routes.swift | 6 ++++-- Sources/ExpressionParser/ExpressionParser.swift | 9 --------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Public/index.html b/Public/index.html index 13110c6..7a7b38f 100644 --- a/Public/index.html +++ b/Public/index.html @@ -189,6 +189,7 @@

Regex Debugger

Resets Backtracks + 0 @@ -280,7 +281,7 @@

Regex Debugger

- Donate + Donate
diff --git a/Public/js/app.js b/Public/js/app.js index a047605..570d4ae 100644 --- a/Public/js/app.js +++ b/Public/js/app.js @@ -79,6 +79,11 @@ export class App { this.onDebuggerStepChange(); }); + const matchStepRange = document.getElementById("debugger-step-range"); + matchStepRange.addEventListener("input", () => { + this.onDebuggerStepChange(); + }); + this.debuggerModal = document.getElementById("debugger-modal"); this.debuggerModal.addEventListener("shown.bs.modal", () => this.launchDebugger(), @@ -219,10 +224,6 @@ export class App { this.debuggerText.value = text; this.onDebuggerStepChange(); - - matchStepRange.addEventListener("input", (e) => { - this.onDebuggerStepChange(); - }); } onExpressionFieldChange() { @@ -450,7 +451,7 @@ export class App { const backtracks = document.getElementById("debugger-backtracks"); - const previousBacktracks = backtracks.textContent; + const previousBacktracks = Number(backtracks.textContent); backtracks.textContent = metrics.backtracks; this.debuggerText.highlighter.draw( diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index fa419b1..1533c51 100644 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -188,14 +188,16 @@ func routes(_ app: Application) throws { process.standardError = standardError try process.run() - process.waitUntilExit() let stdoutData = standardOutput.fileHandleForReading.readDataToEndOfFile() + let stderrData = standardError.fileHandleForReading.readDataToEndOfFile() + + process.waitUntilExit() + guard let stdout = String(data: stdoutData, encoding: .utf8) else { throw Abort(.internalServerError) } - let stderrData = standardError.fileHandleForReading.readDataToEndOfFile() guard let stderr = String(data: stderrData, encoding: .utf8) else { throw Abort(.internalServerError) } diff --git a/Sources/ExpressionParser/ExpressionParser.swift b/Sources/ExpressionParser/ExpressionParser.swift index 7e6bbd3..7d8036a 100644 --- a/Sources/ExpressionParser/ExpressionParser.swift +++ b/Sources/ExpressionParser/ExpressionParser.swift @@ -119,45 +119,36 @@ struct ExpressionParser { category = "groups" key = "noncapgroup" case .nonCaptureReset: - groupCount += 1 category = "groups" key = "branchreset" case .atomicNonCapturing: - groupCount += 1 category = "groups" key = "atomic" case .lookahead: category = "lookaround" key = "poslookahead" case .negativeLookahead: - groupCount += 1 category = "lookaround" key = "neglookahead" case .nonAtomicLookahead: - groupCount += 1 category = "lookaround" key = "nonatomicposlookahead" case .lookbehind: category = "lookaround" key = "poslookbehind" case .negativeLookbehind: - groupCount += 1 category = "lookaround" key = "neglookbehind" case .nonAtomicLookbehind: - groupCount += 1 category = "lookaround" key = "nonatomicposlookbehind" case .scriptRun: - groupCount += 1 category = "Script run. " key = "" case .atomicScriptRun: - groupCount += 1 category = "Atomic script run. " key = "" case .changeMatchingOptions(_): - groupCount += 1 category = "Change matching options" key = "" } From eaff15dbc13f5302efd38e1e18da2485311758be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:07:57 +0000 Subject: [PATCH 2/2] Fix concurrent stdout/stderr draining in exec() to prevent pipe deadlock Agent-Logs-Url: https://github.com/SwiftFiddle/swiftregex/sessions/0581c0b2-a6c3-4457-a084-a2f15745cd99 Co-authored-by: kishikawakatsumi <40610+kishikawakatsumi@users.noreply.github.com> --- Sources/App/routes.swift | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index 1533c51..21e383b 100644 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -187,11 +187,35 @@ func routes(_ app: Application) throws { process.standardOutput = standardOutput process.standardError = standardError - try process.run() + var stdoutData = Data() + var stderrData = Data() + let group = DispatchGroup() + + group.enter() + standardOutput.fileHandleForReading.readabilityHandler = { handle in + let chunk = handle.availableData + if chunk.isEmpty { + standardOutput.fileHandleForReading.readabilityHandler = nil + group.leave() + } else { + stdoutData.append(chunk) + } + } - let stdoutData = standardOutput.fileHandleForReading.readDataToEndOfFile() - let stderrData = standardError.fileHandleForReading.readDataToEndOfFile() + group.enter() + standardError.fileHandleForReading.readabilityHandler = { handle in + let chunk = handle.availableData + if chunk.isEmpty { + standardError.fileHandleForReading.readabilityHandler = nil + group.leave() + } else { + stderrData.append(chunk) + } + } + + try process.run() + group.wait() process.waitUntilExit() guard let stdout = String(data: stdoutData, encoding: .utf8) else {