From ef31e2e770f9fa45eb1d09868a751c342082b527 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:53:26 +0000 Subject: [PATCH 1/4] Initial plan From 18721bd8f3fc266b4cff3868fb98e954e0fb16de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:05:14 +0000 Subject: [PATCH 2/4] Optimize hot paths: reduce allocations and improve loop efficiency Co-authored-by: CapsAdmin <204157+CapsAdmin@users.noreply.github.com> --- nattlua/parser/node.lua | 48 ++++++++++++++++++++++------------------ nattlua/types/number.lua | 16 +------------- nattlua/types/table.lua | 5 ++++- nattlua/types/union.lua | 37 ++++++++++++++++++++----------- 4 files changed, 56 insertions(+), 50 deletions(-) diff --git a/nattlua/parser/node.lua b/nattlua/parser/node.lua index 38cf5771..2f60afb1 100644 --- a/nattlua/parser/node.lua +++ b/nattlua/parser/node.lua @@ -1034,31 +1034,37 @@ local NAN_TYPE = {} local ANY_TYPE = {} function META:AssociateType(obj) - self.inferred_types_done = self.inferred_types_done or {} - self.inferred_types = self.inferred_types or {} - - do - local t = obj.Type - local hash = obj - - if hash.Type == "symbol" then - hash = obj.Data - elseif obj.Type == "string" then - hash = obj.Data or STRING_TYPE - elseif obj.Type == "number" then - hash = obj.Data or NUMBER_TYPE - - if hash ~= hash then hash = NAN_TYPE end - elseif obj.Type == "any" then - hash = ANY_TYPE - end + local done = self.inferred_types_done + local types = self.inferred_types + + if not done then + done = {} + self.inferred_types_done = done + end + + if not types then + types = {} + self.inferred_types = types + end - if self.inferred_types_done[hash] then return end + local obj_type = obj.Type + local hash = obj - self.inferred_types_done[hash] = true + if obj_type == "symbol" then + hash = obj.Data + elseif obj_type == "string" then + hash = obj.Data or STRING_TYPE + elseif obj_type == "number" then + hash = obj.Data or NUMBER_TYPE + if hash ~= hash then hash = NAN_TYPE end + elseif obj_type == "any" then + hash = ANY_TYPE end - self.inferred_types[#self.inferred_types + 1] = obj + if done[hash] then return end + + done[hash] = true + types[#types + 1] = obj end function META:GetAssociatedTypes() diff --git a/nattlua/types/number.lua b/nattlua/types/number.lua index 4a9f04aa..e68fd50f 100644 --- a/nattlua/types/number.lua +++ b/nattlua/types/number.lua @@ -92,21 +92,7 @@ end function META.Equal(a--[[#: TNumber]], b--[[#: TBaseType]]) if a.Type ~= b.Type then return false, "types differ" end - do - return a.Hash == b.Hash - end - - if a.Data == b.Data then return true, "max values are equal" end - - if not a.Data and not b.Data then - return true, "no literal data in either value" - else - if a:IsNan() and b:IsNan() then return true, "both values are nan" end - - return a.Data == b.Data, "literal values are equal" - end - - return false, "values are not equal" + return a.Hash == b.Hash, "hash values are equal" end function META:IsLiteral() diff --git a/nattlua/types/table.lua b/nattlua/types/table.lua index 27a5f0aa..8c8079c3 100644 --- a/nattlua/types/table.lua +++ b/nattlua/types/table.lua @@ -675,8 +675,11 @@ function META:FindKeyValWide(key--[[#: TBaseType]], reverse--[[#: boolean | nil] if keyval then return keyval end local reasons = {} + local data = self.Data + local len = #data - for i, keyval in ipairs(self.Data) do + for i = 1, len do + local keyval = data[i] if key:Equal(keyval.key) then return keyval end local ok, reason diff --git a/nattlua/types/union.lua b/nattlua/types/union.lua index 89758414..a6819ed0 100644 --- a/nattlua/types/union.lua +++ b/nattlua/types/union.lua @@ -81,8 +81,9 @@ function META.Equal( end function META:GetHash(visited)--[[#: string]] - if self:GetCardinality() == 1 then - return (self.Data[1]--[[# as any]]):GetHash() + local data = self.Data + if #data == 1 then + return (data[1]--[[# as any]]):GetHash() end visited = visited or {} @@ -91,14 +92,16 @@ function META:GetHash(visited)--[[#: string]] visited[self] = "*circular*" local types = {} + local len = #data - for i, v in ipairs(self.Data) do - types[i] = v:GetHash(visited) + for i = 1, len do + types[i] = data[i]:GetHash(visited) end table_sort(types) - visited[self] = table.concat(types, "|") - return visited[self]--[[# as string]] + local hash = table_concat(types, "|") + visited[self] = hash + return hash end local sort = function(a--[[#: string]], b--[[#: string]]) @@ -110,9 +113,12 @@ function META:__tostring() local s = {} self.suppress = true + + local data = self.Data + local len = #data - for i, v in ipairs(self.Data) do - s[i] = tostring(v) + for i = 1, len do + s[i] = tostring(data[i]) end if not s[1] then @@ -122,7 +128,7 @@ function META:__tostring() self.suppress = false - if #s == 1 then return (s[1]--[[# as string]]) .. "|" end + if len == 1 then return (s[1]--[[# as string]]) .. "|" end table_sort(s, sort) return table_concat(s, " | ") @@ -155,11 +161,16 @@ local function remove(self--[[#: TUnion]], index--[[#: number]]) end local function find_index(self--[[#: TUnion]], obj--[[#: any]]) - for i = 1, #self.Data do - local v = self.Data[i]--[[# as any]] + local data = self.Data + local len = #data + local obj_type = obj.Type - if v:Equal(obj) then - if v.Type ~= "function" or v:GetFunctionBodyNode() == obj:GetFunctionBodyNode() then + for i = 1, len do + local v = data[i]--[[# as any]] + + -- Early exit if types don't match + if v.Type == obj_type and v:Equal(obj) then + if obj_type ~= "function" or v:GetFunctionBodyNode() == obj:GetFunctionBodyNode() then return i end end From f057650a11e6c58d6d4bc629f8f7e5d55a0e3bc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:10:36 +0000 Subject: [PATCH 3/4] Optimize table and tuple operations: replace ipairs with numeric loops Co-authored-by: CapsAdmin <204157+CapsAdmin@users.noreply.github.com> --- nattlua/types/table.lua | 30 ++++++++++++++++++++++++------ nattlua/types/tuple.lua | 20 ++++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/nattlua/types/table.lua b/nattlua/types/table.lua index 8c8079c3..4245775d 100644 --- a/nattlua/types/table.lua +++ b/nattlua/types/table.lua @@ -224,7 +224,11 @@ function META:__tostring() local contract = self:GetContract() if contract and contract.Type == "table" and contract ~= self then - for i, keyval in ipairs(contract:GetData()) do + local contract_data = contract:GetData() + local contract_len = #contract_data + + for i = 1, contract_len do + local keyval = contract_data[i] local table_kv = self:FindKeyValExact(keyval.key) local key = tostring(table_kv and table_kv.key or "nil") local val = tostring(table_kv and table_kv.val or "nil") @@ -244,7 +248,11 @@ function META:__tostring() end end else - for i, keyval in ipairs(self.Data) do + local data = self.Data + local len = #data + + for i = 1, len do + local keyval = data[i] local key, val = tostring(keyval.key), tostring(keyval.val) s[i] = indent .. "[" .. key .. "]" .. " = " .. val end @@ -264,8 +272,11 @@ function META:GetArrayLength() if contract and contract ~= self then return contract:GetArrayLength() end local len = 0 + local data = self.Data + local data_len = #data - for _, kv in ipairs(self.Data) do + for i = 1, data_len do + local kv = data[i] if kv.key:IsNumeric() then if kv.key:IsLiteral() then -- TODO: not very accurate @@ -335,7 +346,11 @@ function META:FollowsContract(contract--[[#: TTable]]) end end - for _, keyval in ipairs(self.Data) do + local data = self.Data + local len = #data + + for i = 1, len do + local keyval = data[i] if not keyval.val:IsNil() then local res, err = contract:FindKeyValExact(keyval.key) @@ -356,8 +371,11 @@ function META:FollowsContract(contract--[[#: TTable]]) end function META:CanBeEmpty() - for _, keyval in ipairs(self.Data) do - if not keyval.val:CanBeNil() then return false end + local data = self.Data + local len = #data + + for i = 1, len do + if not data[i].val:CanBeNil() then return false end end return true diff --git a/nattlua/types/tuple.lua b/nattlua/types/tuple.lua index 32e92e18..826c97c6 100644 --- a/nattlua/types/tuple.lua +++ b/nattlua/types/tuple.lua @@ -51,9 +51,11 @@ function META:GetHash(visited) visited[self] = "*circular*" local types = {} + local data = self.Data + local len = #data - for i, v in ipairs(self.Data) do - types[i] = v:GetHash(visited) + for i = 1, len do + types[i] = data[i]:GetHash(visited) end visited[self] = table.concat(types, ",") @@ -65,9 +67,11 @@ function META:__tostring() self.suppress = true local strings--[[#: List<|string|>]] = {} + local data = self.Data + local len = #data - for i, v in ipairs(self.Data) do - strings[i] = tostring(v) + for i = 1, len do + strings[i] = tostring(data[i]) end if self.Remainder then table.insert(strings, tostring(self.Remainder)) end @@ -125,9 +129,13 @@ function META:Copy(map--[[#: Map<|any, any|> | nil]], copy_tables) local copy = META.New({}) map[self] = copy + + local data = self.Data + local copy_data = copy.Data + local len = #data - for i, v in ipairs(self.Data) do - copy.Data[i] = copy_val(v, map, copy_tables) + for i = 1, len do + copy_data[i] = copy_val(data[i], map, copy_tables) end copy.Repeat = self.Repeat From 3b3f9b6ea292bfe85a35b72b20d6cfb2f62f94dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:13:33 +0000 Subject: [PATCH 4/4] Address code review: improve comments and simplify variable usage Co-authored-by: CapsAdmin <204157+CapsAdmin@users.noreply.github.com> --- nattlua/types/union.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nattlua/types/union.lua b/nattlua/types/union.lua index a6819ed0..19269b93 100644 --- a/nattlua/types/union.lua +++ b/nattlua/types/union.lua @@ -99,9 +99,8 @@ function META:GetHash(visited)--[[#: string]] end table_sort(types) - local hash = table_concat(types, "|") - visited[self] = hash - return hash + visited[self] = table_concat(types, "|") + return visited[self]--[[# as string]] end local sort = function(a--[[#: string]], b--[[#: string]]) @@ -168,7 +167,7 @@ local function find_index(self--[[#: TUnion]], obj--[[#: any]]) for i = 1, len do local v = data[i]--[[# as any]] - -- Early exit if types don't match + -- Check type first before expensive Equal call if v.Type == obj_type and v:Equal(obj) then if obj_type ~= "function" or v:GetFunctionBodyNode() == obj:GetFunctionBodyNode() then return i