From 76fb1dc9e300f8b341df2551204413a37a57cc0f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 10:25:30 -0700 Subject: [PATCH] perf: eliminate LINQ Skip allocations in ExecShellWrapperParser (#193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace string.Join(", ", tokens.Skip(i + 1)) with the indexed string.Join(string, string[], int, int) overload in the three shell- payload reconstruction sites (cmd /c, bash -c, powershell -Command). Changes: - Tokenize() now returns string[] instead of List; callers already received it as an opaque list so the type narrowing is safe. - ParsePowerShellPayload signature updated to string[] to match. - Three string.Join(" ", tokens.Skip(i + 1)) calls replaced with string.Join(" ", tokens, i + 1, tokens.Length - i - 1) — uses the well-known BCL overload that slices directly into the array with no iterator allocation. - Removes the now-unused 'using System.Linq;' import. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/OpenClaw.Shared/ExecShellWrapperParser.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/OpenClaw.Shared/ExecShellWrapperParser.cs b/src/OpenClaw.Shared/ExecShellWrapperParser.cs index ff568d0..b5640e3 100644 --- a/src/OpenClaw.Shared/ExecShellWrapperParser.cs +++ b/src/OpenClaw.Shared/ExecShellWrapperParser.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; namespace OpenClaw.Shared; @@ -76,7 +75,7 @@ internal static class ExecShellWrapperParser private static (string? Payload, string? Shell, string? Error) TryExtractWrappedPayload(string command) { var tokens = Tokenize(command); - if (tokens.Count < 2) + if (tokens.Length < 2) return default; var executable = Path.GetFileName(tokens[0]); @@ -86,12 +85,12 @@ internal static class ExecShellWrapperParser if (executable.Equals("cmd", StringComparison.OrdinalIgnoreCase) || executable.Equals("cmd.exe", StringComparison.OrdinalIgnoreCase)) { - for (var i = 1; i < tokens.Count; i++) + for (var i = 1; i < tokens.Length; i++) { if (tokens[i].Equals("/c", StringComparison.OrdinalIgnoreCase) || tokens[i].Equals("/k", StringComparison.OrdinalIgnoreCase)) { - var payload = string.Join(" ", tokens.Skip(i + 1)).Trim(); + var payload = string.Join(" ", tokens, i + 1, tokens.Length - i - 1).Trim(); return string.IsNullOrWhiteSpace(payload) ? ("", "cmd", "Shell wrapper payload was empty") : (payload, "cmd", null); @@ -116,11 +115,11 @@ internal static class ExecShellWrapperParser executable.Equals("sh", StringComparison.OrdinalIgnoreCase) || executable.Equals("sh.exe", StringComparison.OrdinalIgnoreCase)) { - for (var i = 1; i < tokens.Count; i++) + for (var i = 1; i < tokens.Length; i++) { if (tokens[i].Equals("-c", StringComparison.OrdinalIgnoreCase)) { - var payload = string.Join(" ", tokens.Skip(i + 1)).Trim(); + var payload = string.Join(" ", tokens, i + 1, tokens.Length - i - 1).Trim(); return string.IsNullOrWhiteSpace(payload) ? ("", "sh", "Shell wrapper payload was empty") : (payload, "sh", null); @@ -131,15 +130,15 @@ internal static class ExecShellWrapperParser return default; } - private static (string? Payload, string? Shell, string? Error) ParsePowerShellPayload(IReadOnlyList tokens, string shell) + private static (string? Payload, string? Shell, string? Error) ParsePowerShellPayload(string[] tokens, string shell) { - for (var i = 1; i < tokens.Count; i++) + for (var i = 1; i < tokens.Length; i++) { var option = tokens[i]; if (option.Equals("-Command", StringComparison.OrdinalIgnoreCase) || option.Equals("-c", StringComparison.OrdinalIgnoreCase)) { - var payload = string.Join(" ", tokens.Skip(i + 1)).Trim(); + var payload = string.Join(" ", tokens, i + 1, tokens.Length - i - 1).Trim(); return string.IsNullOrWhiteSpace(payload) ? ("", shell, "Shell wrapper payload was empty") : (payload, shell, null); @@ -149,7 +148,7 @@ internal static class ExecShellWrapperParser option.Equals("-enc", StringComparison.OrdinalIgnoreCase) || option.Equals("-ec", StringComparison.OrdinalIgnoreCase)) { - var encoded = i + 1 < tokens.Count ? tokens[i + 1] : null; + var encoded = i + 1 < tokens.Length ? tokens[i + 1] : null; if (string.IsNullOrWhiteSpace(encoded)) return ("", shell, "Shell wrapper payload was empty"); @@ -221,7 +220,7 @@ internal static class ExecShellWrapperParser return parts; } - private static List Tokenize(string command) + private static string[] Tokenize(string command) { var tokens = new List(); var current = new StringBuilder(); @@ -266,7 +265,7 @@ internal static class ExecShellWrapperParser } FlushCurrent(tokens, current); - return tokens; + return tokens.ToArray(); } private static string TrimMatchingQuotes(string value)