From bd09a5de7e6b067386e0fdacc69d44b460e5d736 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 18:38:29 +0300 Subject: [PATCH 01/13] Add more regex checks to strings in E2 --- .../gmod_wire_expression2/core/string.lua | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/core/string.lua b/lua/entities/gmod_wire_expression2/core/string.lua index 6d441c3528..184cec0c3b 100644 --- a/lua/entities/gmod_wire_expression2/core/string.lua +++ b/lua/entities/gmod_wire_expression2/core/string.lua @@ -232,15 +232,33 @@ end __e2setcost(2) e2function string string:trim() - return this:Trim() + local ok, ret = pcall(function() WireLib.CheckRegex(this, "^%s*(.-)%s*$") return string.Trim(this) end) + + if not ok then + return self:throw(ret) + else + return ret + end end e2function string string:trimLeft() - return this:TrimLeft() + local ok, ret = pcall(function() WireLib.CheckRegex(this, "^%s*(.+)$") return string.TrimLeft(this) end) + + if not ok then + return self:throw(ret) + else + return ret + end end e2function string string:trimRight() - return this:TrimRight() + local ok, ret = pcall(function() WireLib.CheckRegex(this, "^(.-)%s*$") return string.TrimRight(this) end) + + if not ok then + return self:throw(ret) + else + return ret + end end --[[******************************************************************************]]-- @@ -250,8 +268,9 @@ __e2setcost(10) --- Returns the 1st occurrence of the string , returns 0 if not found. Prints malformed string errors to the chat area. e2function number string:findRE(string pattern) local ok, ret = pcall(function() WireLib.CheckRegex(this, pattern) return string_find(this, pattern) end) + if not ok then - return self:throw(ret, 0) + return self:throw(ret) else return ret or 0 end @@ -260,10 +279,11 @@ end --- Returns the 1st occurrence of the string starting at and going to the end of the string, returns 0 if not found. Prints malformed string errors to the chat area. e2function number string:findRE(string pattern, start) local ok, ret = pcall(function() WireLib.CheckRegex(this, pattern) return string_find(this, pattern, start) end) + if not ok then - return self:throw(ret, 0) + return self:throw(ret) else - return ret or 0 + return ret end end From 5d9e02f7fd9eaaaa31aab5c0ef6706f120bc5343 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 20:36:42 +0300 Subject: [PATCH 02/13] Make whitespace trimming use another mechanism --- .../base/preprocessor.lua | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index ca95b7e55b..81169cb6bc 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -83,6 +83,17 @@ function PreProcessor:HandlePPCommand(comment, col) end end +function PreProcessor:TrimWhitespace(line) + for i = #line, 1, -1 do + if string.byte(line, i) ~= 32 then + return string.sub(line, 1, i) + end + end + + -- The line consists only of spaces + return "" +end + function PreProcessor:FindComments(line) local isinput = not self.blockcomment and not self.multilinestring and line:match("^@inputs") ~= nil local isoutput = not self.blockcomment and not self.multilinestring and line:match("^@outputs") ~= nil @@ -403,17 +414,13 @@ function PreProcessor:Process(buffer, directives, ent) self.ignorestuff = true end - -- to avoid big hangs, 2 regex changed from 500 to 10000 to avoid false positives - local regex_limits = {[0] = 50000000, 15000, 10000, 150, 70, 40} + -- to avoid big hangs local timeout = SysTime() + 0.5 for i, line in ipairs(lines) do self.readline = i - local ok = pcall(function() WireLib.CheckRegex(line, "^(.-)%s*$", regex_limits) end) - if not ok then self:Error("Line strip regex is too complex!") goto cont end - - line = string.TrimRight(line) + line = self:TrimWhitespace(line) line = self:RemoveComments(line) line = self:ParseDirectives(line) lines[i] = line From 0ab3e79e7bed5c0c3d19b355277c7de0154406c9 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 20:41:52 +0300 Subject: [PATCH 03/13] Change trailing whitespace trimming way + limit max line lenght to 1m --- lua/entities/gmod_wire_expression2/base/preprocessor.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 81169cb6bc..dd45c44702 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -420,6 +420,11 @@ function PreProcessor:Process(buffer, directives, ent) for i, line in ipairs(lines) do self.readline = i + if #line > 1000000 then + self:Error("Line is too long") + break + end + line = self:TrimWhitespace(line) line = self:RemoveComments(line) line = self:ParseDirectives(line) @@ -427,7 +432,7 @@ function PreProcessor:Process(buffer, directives, ent) ::cont:: if SysTime() > timeout then - self:Error("Preprocessing took too long!") + self:Error("Preprocessing took too long") break end end From bacc5d85011936e4c066c3763dfd4b55c13b333a Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 21:15:20 +0300 Subject: [PATCH 04/13] Change trimming mechanism more --- .../base/preprocessor.lua | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index dd45c44702..210d46312b 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -64,7 +64,7 @@ local type_map = { } function PreProcessor:GetType(tp, trace) - tp = tp:Trim():lower() + tp = self:Trim(tp):lower() local up = tp:upper() if tp == "normal" then @@ -83,7 +83,38 @@ function PreProcessor:HandlePPCommand(comment, col) end end -function PreProcessor:TrimWhitespace(line) +function PreProcessor:Trim(line) + local length = #line + local first + + for i = 1, length do + if string.byte(line, i) ~= 32 then + first = i + break + end + end + + if not first then + return "" + end + + local last + + for i = length, 1, -1 do + if string.byte(line, i) ~= 32 then + last = i + break + end + end + + if not last then + return "" + end + + return string.sub(line, first, last) +end + +function PreProcessor:TrimRight(line) for i = #line, 1, -1 do if string.byte(line, i) ~= 32 then return string.sub(line, 1, i) @@ -276,7 +307,7 @@ local directive_handlers = { ["persist"] = handleIO("persist"), ["trigger"] = function(self, value, trace) - local trimmed = string.Trim(value) + local trimmed = PreProcessor.Trim(value) if trimmed == "all" then if self.directives.trigger[1] ~= nil then self:Error("Directive (@trigger) conflicts with previous directives", trace) @@ -322,7 +353,7 @@ local directive_handlers = { end if CLIENT then - if #string.Trim(arg) > 0 then + if #PreProcessor.Trim(arg) > 0 then trace.start_col = trace.end_col + 1 trace.end_line = trace.start_line + 1 trace.end_col = 1 @@ -359,7 +390,7 @@ function PreProcessor:ParseDirectives(line) -- not a directive? if not directive then -- flag as "in code", if that is the case - if string.Trim(line) ~= "" then + if self:Trim(line) ~= "" then self.incode = true end -- don't handle as a directive. @@ -420,12 +451,7 @@ function PreProcessor:Process(buffer, directives, ent) for i, line in ipairs(lines) do self.readline = i - if #line > 1000000 then - self:Error("Line is too long") - break - end - - line = self:TrimWhitespace(line) + line = self:TrimRight(line) line = self:RemoveComments(line) line = self:ParseDirectives(line) lines[i] = line @@ -482,7 +508,7 @@ function PreProcessor:ParsePorts(ports, startoffset) column2 = column + column2 local tr = Trace.new(self.readline, column2, self.readline, column2 + #var) - var = string.Trim(var) + var = self:Trim(var) -- skip empty entries if var ~= "" then -- error on malformed variable names @@ -627,7 +653,7 @@ function PreProcessor:PP_else(args, trace) local state = table.remove(self.ifdefStack) if state == nil then self:Error("Found #else outside #ifdef/#ifndef block", trace) end - if args:Trim() ~= "" then self:Error("Must not pass an argument to #else", trace) end + if self:Trim(args) ~= "" then self:Error("Must not pass an argument to #else", trace) end if self:Disabled() then table.insert(self.ifdefStack, false) @@ -640,7 +666,7 @@ function PreProcessor:PP_endif(args, trace) local state = table.remove(self.ifdefStack) if state == nil then self:Error("Found #endif outside #ifdef/#ifndef block", trace) end - if args:Trim() ~= "" then self:Error("Must not pass an argument to #endif", trace) end + if self:Trim(args) ~= "" then self:Error("Must not pass an argument to #endif", trace) end end function PreProcessor:PP_error(args, trace) From c440c717a218570dc8e99642e0188ab45fc383f4 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 22:26:32 +0300 Subject: [PATCH 05/13] Useless check --- lua/entities/gmod_wire_expression2/base/preprocessor.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 210d46312b..9e67010756 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -107,10 +107,6 @@ function PreProcessor:Trim(line) end end - if not last then - return "" - end - return string.sub(line, first, last) end From d90c5e0acd189e899acf9aeb867d5c3e00fe5643 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 22:50:58 +0300 Subject: [PATCH 06/13] Fix trim nil error --- lua/entities/gmod_wire_expression2/base/preprocessor.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 9e67010756..94c7933b2b 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -303,7 +303,7 @@ local directive_handlers = { ["persist"] = handleIO("persist"), ["trigger"] = function(self, value, trace) - local trimmed = PreProcessor.Trim(value) + local trimmed = PreProcessor.Trim(nil, value) if trimmed == "all" then if self.directives.trigger[1] ~= nil then self:Error("Directive (@trigger) conflicts with previous directives", trace) @@ -349,7 +349,7 @@ local directive_handlers = { end if CLIENT then - if #PreProcessor.Trim(arg) > 0 then + if #PreProcessor.Trim(nil, arg) > 0 then trace.start_col = trace.end_col + 1 trace.end_line = trace.start_line + 1 trace.end_col = 1 From 4fe438a29f93aa77654489d7e65a82db0d6c8d6b Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 23:09:10 +0300 Subject: [PATCH 07/13] Fix e2 tests regression --- .../gmod_wire_expression2/base/preprocessor.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 94c7933b2b..78c502e5da 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -83,12 +83,21 @@ function PreProcessor:HandlePPCommand(comment, col) end end +local space_chars = { + [9] = true, + [10] = true, + [11] = true, + [12] = true, + [13] = true, + [32] = true +} + function PreProcessor:Trim(line) local length = #line local first for i = 1, length do - if string.byte(line, i) ~= 32 then + if not space_chars[string.byte(line, i)] then first = i break end @@ -101,7 +110,7 @@ function PreProcessor:Trim(line) local last for i = length, 1, -1 do - if string.byte(line, i) ~= 32 then + if not space_chars[string.byte(line, i)] then last = i break end @@ -112,7 +121,7 @@ end function PreProcessor:TrimRight(line) for i = #line, 1, -1 do - if string.byte(line, i) ~= 32 then + if not space_chars[string.byte(line, i)] then return string.sub(line, 1, i) end end From 796260a429cc8434143759cb2c22b7b0cda3dbc9 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 29 May 2026 23:47:09 +0300 Subject: [PATCH 08/13] Try optimizing FindComments --- .../base/preprocessor.lua | 83 ++++++++++++------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 78c502e5da..e1714b47a3 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -134,43 +134,62 @@ function PreProcessor:FindComments(line) local isinput = not self.blockcomment and not self.multilinestring and line:match("^@inputs") ~= nil local isoutput = not self.blockcomment and not self.multilinestring and line:match("^@outputs") ~= nil - local ret, count, pos, found = {}, 0, 1 - repeat - found = line:find((isinput or isoutput) and '[#"\\A-Z]' or '[#"\\]', pos) - if found then -- We found something - local char = line:sub(found, found) - if (isinput or isoutput) and char:match("[A-Z]") ~= nil then -- we found the start of an input/output variable definition - local varname, endpos = line:match("^([A-Z][A-Za-z0-9_]*)()",found) - count = count + 1 - ret[count] = {type = isinput and "inputs" or "outputs", name=varname, pos=found, blockcomment = {}} - pos = endpos - elseif char == "#" then -- We found a comment - local before = line:sub(found - 1, found - 1) - if before == "]" then -- We found an ending - count = count + 1 - ret[count] = { type = "end", pos = found - 1 } - pos = found + 1 + local ret, count = {}, 0 + local len = #line + local i = 1 + + while i <= len do + local byte = string.byte(line, i) + + -- We found the start of an input/output variable definition + if (isinput or isoutput) and byte >= 65 and byte <= 90 then + local start = i + i = i + 1 + + while i <= len do + local b = string.byte(line, i) + + if (b >= 65 and b <= 90) or (b >= 97 and b <= 122) or (b >= 48 and b <= 57) or b == 95 then + i = i + 1 else - local after = line:sub(found + 1, found + 1) - if after == "[" then -- We found a start - count = count + 1 - ret[count] = { type = "start", pos = found } - pos = found + 2 - else -- We found a normal comment - count = count + 1 - ret[count] = { type = "normal", pos = found } - pos = found + 1 - end + break end - elseif char == '"' then -- We found a string + end + + local varname = string.sub(line, start, i - 1) + count = count + 1 + ret[count] = { type = isinput and "inputs" or "outputs", name = varname, pos = start, blockcomment = {} } + elseif byte == 92 then -- We found an escape character + i = i + 2 -- Skip the escape character and the character following it + elseif byte == 34 then -- We found a string + count = count + 1 + ret[count] = { type = "string", pos = i } + i = i + 1 + elseif byte == 35 then -- We found a comment + local before = i > 1 and string.byte(line, i - 1) + + if before == 93 then -- We found an ending count = count + 1 - ret[count] = { type = "string", pos = found } - pos = found + 1 - elseif char == '\\' then -- We found an escape character - pos = found + 2 -- Skip the escape character and the character following it + ret[count] = { type = "end", pos = i - 1 } + i = i + 1 + else + local after = i < len and string.byte(line, i + 1) + + if after == 91 then -- We found a start + count = count + 1 + ret[count] = { type = "start", pos = i } + i = i + 2 + else -- We found a normal comment + count = count + 1 + ret[count] = { type = "normal", pos = i } + i = i + 1 + end end + else + i = i + 1 end - until (not found) + end + return ret, count end From 48ed4a632c4c03004387d2c7d1b21ba018c1e634 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Sat, 30 May 2026 00:13:45 +0300 Subject: [PATCH 09/13] Limit max chip size to 5mb --- .../base/preprocessor.lua | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index e1714b47a3..a46342e8f7 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -453,8 +453,6 @@ function PreProcessor:Process(buffer, directives, ent) self.ifdefStack = {} self.warnings, self.errors = {}, {} - local lines = string.Explode("\n", buffer) - if not directives then self.directives = { name = nil, @@ -469,21 +467,28 @@ function PreProcessor:Process(buffer, directives, ent) self.ignorestuff = true end - -- to avoid big hangs - local timeout = SysTime() + 0.5 + local lines = {} - for i, line in ipairs(lines) do - self.readline = i + if #buffer > 5000000 then -- 5mb + self:Error("Buffer is too big") + lines = {} + else + -- to avoid big hangs + local timeout = SysTime() + 0.5 + lines = string.Explode("\n", buffer) - line = self:TrimRight(line) - line = self:RemoveComments(line) - line = self:ParseDirectives(line) - lines[i] = line - ::cont:: + for i, line in ipairs(lines) do + self.readline = i - if SysTime() > timeout then - self:Error("Preprocessing took too long") - break + line = self:TrimRight(line) + line = self:RemoveComments(line) + line = self:ParseDirectives(line) + lines[i] = line + + if SysTime() > timeout then + self:Error("Preprocessing took too long") + break + end end end From e1bb35db7d0c9d34cc7e9301dcc0dc45ef7d88b0 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Sat, 30 May 2026 00:14:14 +0300 Subject: [PATCH 10/13] Don't define table --- lua/entities/gmod_wire_expression2/base/preprocessor.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index a46342e8f7..7f5c739874 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -467,7 +467,7 @@ function PreProcessor:Process(buffer, directives, ent) self.ignorestuff = true end - local lines = {} + local lines if #buffer > 5000000 then -- 5mb self:Error("Buffer is too big") From dab56293d98af07a9f547c911e7c301a640045bf Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Sat, 30 May 2026 00:24:53 +0300 Subject: [PATCH 11/13] Make the error clearer --- lua/entities/gmod_wire_expression2/base/preprocessor.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 7f5c739874..5cbba8b743 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -470,7 +470,7 @@ function PreProcessor:Process(buffer, directives, ent) local lines if #buffer > 5000000 then -- 5mb - self:Error("Buffer is too big") + self:Error("Code is too big (5mb max)") lines = {} else -- to avoid big hangs From 13d18ea5065e8a653aa39bcc1a638010b9c2f61e Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Sat, 30 May 2026 02:35:43 +0300 Subject: [PATCH 12/13] Don't use hash table for this --- .../base/preprocessor.lua | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua index 5cbba8b743..2868e508f6 100644 --- a/lua/entities/gmod_wire_expression2/base/preprocessor.lua +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -83,21 +83,14 @@ function PreProcessor:HandlePPCommand(comment, col) end end -local space_chars = { - [9] = true, - [10] = true, - [11] = true, - [12] = true, - [13] = true, - [32] = true -} - function PreProcessor:Trim(line) local length = #line local first for i = 1, length do - if not space_chars[string.byte(line, i)] then + local b = string.byte(line, i) + + if b ~= 32 and (b < 9 or b > 13) then first = i break end @@ -110,7 +103,9 @@ function PreProcessor:Trim(line) local last for i = length, 1, -1 do - if not space_chars[string.byte(line, i)] then + local b = string.byte(line, i) + + if b ~= 32 and (b < 9 or b > 13) then last = i break end @@ -121,7 +116,9 @@ end function PreProcessor:TrimRight(line) for i = #line, 1, -1 do - if not space_chars[string.byte(line, i)] then + local b = string.byte(line, i) + + if b ~= 32 and (b < 9 or b > 13) then return string.sub(line, 1, i) end end From ab48fb0e1c01a0a6801c9ed47c7f39de4b1907d1 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Sat, 30 May 2026 02:47:16 +0300 Subject: [PATCH 13/13] Readd nil check --- lua/entities/gmod_wire_expression2/core/string.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_expression2/core/string.lua b/lua/entities/gmod_wire_expression2/core/string.lua index 184cec0c3b..eb62f04a33 100644 --- a/lua/entities/gmod_wire_expression2/core/string.lua +++ b/lua/entities/gmod_wire_expression2/core/string.lua @@ -283,7 +283,7 @@ e2function number string:findRE(string pattern, start) if not ok then return self:throw(ret) else - return ret + return ret or 0 end end